From 45da765eb84dd04d64f512b309bbfddbd8269e67 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 22 Nov 2025 11:36:23 -0600 Subject: [PATCH 01/15] Update Muzi R1 Neo to actively supported --- Meshtastic/Resources/DeviceHardware.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Meshtastic/Resources/DeviceHardware.json b/Meshtastic/Resources/DeviceHardware.json index 1306cfd2..759a21d3 100644 --- a/Meshtastic/Resources/DeviceHardware.json +++ b/Meshtastic/Resources/DeviceHardware.json @@ -1051,7 +1051,7 @@ "hwModelSlug": "MUZI_R1_NEO", "platformioTarget": "r1-neo", "architecture": "nrf52840", - "activelySupported": false, + "activelySupported": true, "supportLevel": 1, "displayName": "muzi R1 Neo", "tags": [ @@ -1183,3 +1183,4 @@ ] } ] + From 4f5721717f867415228720a994fd12c534c9cfaa Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 17 Dec 2025 20:46:41 -0600 Subject: [PATCH 02/15] Revert "Update Muzi R1 Neo to actively supported" --- Meshtastic/Resources/DeviceHardware.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Meshtastic/Resources/DeviceHardware.json b/Meshtastic/Resources/DeviceHardware.json index 759a21d3..1306cfd2 100644 --- a/Meshtastic/Resources/DeviceHardware.json +++ b/Meshtastic/Resources/DeviceHardware.json @@ -1051,7 +1051,7 @@ "hwModelSlug": "MUZI_R1_NEO", "platformioTarget": "r1-neo", "architecture": "nrf52840", - "activelySupported": true, + "activelySupported": false, "supportLevel": 1, "displayName": "muzi R1 Neo", "tags": [ @@ -1183,4 +1183,3 @@ ] } ] - From b30dc8645a9db67bf27f56e4d97132a1bac3bed2 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 21 Dec 2025 12:15:01 -0800 Subject: [PATCH 03/15] 2.7.6 Working Changes (#1479) * Bump version * Message list performance fixes into 2.7.6 (#1475) * Remove extra want config call when adding a contact * App badge and unnecessary notification fixes (#1455) * - Fix for app badge not going to zero if a message arrives while you have that chat open - Fix for push notifications popping up when a message is received while that chat is open * Fix for cancelling notifications, works now. And scroll to bottom of conversation upon new message * Fix: Channels help grammer fix (#1452) * remove outdated TCP not available on Apple devices (#1450) * Update initial onboarding view * remove toggle gating for mac * Update crash reporting opt out in real time * Update onboarding text * Use mDNS text records for node name * TCP IP and port on the connection screen * Hide app icon chooser on mac * Infinite loop hang bugfixes and performance improvements for both `UserMessageList` and `ChannelMessageList` (#1465) * 2.7.5 Working Changes (#1460) * Remove extra want config call when adding a contact * App badge and unnecessary notification fixes (#1455) * - Fix for app badge not going to zero if a message arrives while you have that chat open - Fix for push notifications popping up when a message is received while that chat is open * Fix for cancelling notifications, works now. And scroll to bottom of conversation upon new message * Fix: Channels help grammer fix (#1452) * remove outdated TCP not available on Apple devices (#1450) * Update initial onboarding view * remove toggle gating for mac * Update crash reporting opt out in real time * Update onboarding text --------- Co-authored-by: Gnome Adrift <646322+gnomeadrift@users.noreply.github.com> Co-authored-by: Zain Kergaye <62440012+ZainKergaye@users.noreply.github.com> Co-authored-by: NillRudd <102033730+NillRudd@users.noreply.github.com> * UserEntity: add mostRecentMessage and unreadMessages with early exit when lastMessage is nil, and fetch 1 row (not N) otherwise * UserList: replace 5 slow calls to user.messageList with new fast calls * NodeList: always put the connected node at the top of list (if it matches the node filters) * ChannelEntity: add faster mostRecentPrivateMessage and unreadMessages which fetch 1 row (not N) * ChannelList: replace 5 calls to channel.allPrivateMessage with new fast calls * Fix incorrect appState.unreadDirectMessages calculations * MyInfoEntity: also fix unreadMessages count here to be fast, and use it for appState.unreadChannelMessages * UserMessageList: use @FetchRequest to prevent the N^2 behavior that was happening in calls to allPrivateMessages * Refactor ChannelEntityExtension and MyInfoEntityExtension to be more similar to UserEntityExtension * Remove SwiftUI-infinite-loop-causing `.id(redrawTapbacksTrigger)` in ChannelMessageList and UserMessageList (duplicate row ids) * MyInfoEntityExtension: exclude emoji tapbacks (which never get marked as read anyway) from unread message count * Add SaveChannelLinkData so MessageText and MeshtasticApp can use .sheet(item: ...) and avoid infinite loop hang due to Binding rebuild * ChannelMessageList and UserMessageList: switch to stable messageId for ForEach SwiftUI row identity * ChannelMessageList and UserMessageList: debouncedScrollToBottom; keyboardWillShowNotification/keyboardDidShowNotification * ChannelMessageList and UserMessageList: scroll to bottom onFirstAppear * ChannelMessageList and UserMessageList: block spurious markMessagesAsRead when this View is not active --------- Co-authored-by: Garth Vander Houwen Co-authored-by: Gnome Adrift <646322+gnomeadrift@users.noreply.github.com> Co-authored-by: Zain Kergaye <62440012+ZainKergaye@users.noreply.github.com> Co-authored-by: NillRudd <102033730+NillRudd@users.noreply.github.com> * message-list-performance: revert scrolling changes (#1472) * Revert e0f0b4a0f749d2e83946f2c1297e5c97c9fdf46e (ChannelMessageList and UserMessageList: scroll to bottom onFirstAppear) * Revert "ChannelMessageList and UserMessageList: debouncedScrollToBottom; keyboardWillShowNotification/keyboardDidShowNotification" This reverts commit ee1a7c44157eb6970ec2111fc1ac4d67a44a8238. --------- Co-authored-by: Gnome Adrift <646322+gnomeadrift@users.noreply.github.com> Co-authored-by: Zain Kergaye <62440012+ZainKergaye@users.noreply.github.com> Co-authored-by: NillRudd <102033730+NillRudd@users.noreply.github.com> Co-authored-by: Jake-B Co-authored-by: Mike Robbins * Explicitly set unmessagable, seems unnessary * Add back missing mesh map features * Fix: "Retrieving nodes" significantly slower after reconnect extracted from #1424 (#1477) * Fix: "Retrieving nodes" significantly slower after reconnect (#1424) The node database retrieval was calling context.save() for every single NodeInfo packet received (250 saves for 250 nodes). This caused severe performance degradation on reconnect when CoreData had accumulated state. Root Cause: - nodeInfoPacket() called context.save() immediately for each node - With 250 nodes, this meant 250 individual CoreData save operations - On first connection, CoreData is fresh and fast - On reconnect, CoreData has accumulated change tracking, undo management, and memory pressure, making each save progressively slower - This resulted in 10+ second retrieval times vs 1-2 seconds initially Solution: - Added deferSave parameter to nodeInfoPacket() function - During database retrieval (.retrievingDatabase state), defer all saves - Perform a single batch save when database retrieval completes (when NONCE_ONLY_DB configCompleteID is received) - This reduces 250 saves to 1 save Performance Impact: - Eliminates N individual saves during node database sync - Reduces database retrieval time back to 1-2 seconds on reconnect - Matches first-connection performance consistently Fixes #1424 * Revert *MessageListUnified files --------- Co-authored-by: Martin Bogomolni Co-authored-by: Jake-B * Hide route lines filter from mesh map * Mesh Map: fuzz imprecise locations so they're distinguishable and clickable at the highest zoom levels (#1478) Co-authored-by: Garth Vander Houwen * Fix bad merge * Update Meshtastic/Extensions/CoreData/UserEntityExtension.swift Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update Meshtastic/Views/Nodes/Helpers/Map/MapContent/MeshMapContent.swift Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update Meshtastic/Extensions/CoreData/ChannelEntityExtension.swift Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update Meshtastic/Extensions/CoreData/MyInfoEntityExtension.swift Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update Meshtastic/Extensions/CoreData/ChannelEntityExtension.swift Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Keep list of previous manual connections (#1484) * Keep list of previous manual connections * More descriptive manual connection rows * Merge fixes and new way to show IP on Connect view --------- Co-authored-by: Jake-B * Show who relayed messages (#1486) * Add identification for node that relayed text messages and add count for ammount of relayers of your message * Ack Relays * upsertPositionPacket: don't use future timestamps to set node's lastHeard (#1488) * R1 NEO * Neo * Update Meshtastic/Views/Settings/AppSettings.swift Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Remove bad if * Git rid of extra environment variable * Update Meshtastic/Accessory/Transports/TCP/TCPTransport.swift Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * MeshMap performance: quick wins (#1490) * MeshMap: change onMapCameraChange frequency to .onEnd so that zooming doesn't cause continuous SwiftUI reevaluation on every frame * MeshMapContent: factor out reducedPrecisionMapCircles into a separate function * MeshMapContent: when multiple reducedPrecisionCircles have the same (lat,lon,radius), just draw one (big perf boost in dense areas) * NodeMap performance improvements for high # positions history (#1480) * NodeMapContent: move Route Lines out of ForEach * NodeMapContent: move Convex Hull out of ForEach * NodeMapContent: Replace `position.nodePosition?` with `node` * NodeMapContent: drop unnecessary LazyVStack in showNodeHistory * NodeMapContent: hoist out nodeColorSwift * Move lineCoords, loraCoords calculations within showRouteLines, showConvexHull respectively * Hoist out repeated node.metadata?.positionFlags lookups / PositionFlags creation * NodeMapContent: remove unused @State * NodeMapSwiftUI: add NodeMapContentEquatableWrapper and NodeMapContentSignature to prevent frequent NodeMapContent recomputation and infinite render loops * NodeMapSwiftUI: disable animation during SwiftUI transactions * NodeMapContent: hoist nodeBorderColor and set allowsHitTesting(false) on history point views * NodeMapContent: prerenderHistoryPointCircle and prerenderHistoryPointArrow to avoid thousands of vector draw operations * NodeMapContent: Shared coordinate list for Route Lines and Convex Hull * NodeMapContent.prerenderHistoryPointArrow: add .frame(width: 16, height: 16) * Fix wantRangeTestPackets to correctly follow rangeTestConfig.enabled (#1489) * Fix interval drop down formatter * Clean up channel qr code functionality. * perferredPeripheralId fix * Set opt in * Retry once 5 second timer. dont throw the error * Queue for peripherals * Fix: hoplimit of dms would always fallback to hops away of the node even when configured hops was higher (#1495) * fix hops setting in dms * Fix hops for exchange position * Final fix * Don't favorite client base * Update device hardware * Prevent nil environment metrics * Bump datadog sdk * fix setting device telemetry enabled (#1515) * Update Muzi R1 Neo to actively supported * fix setting device telemetry enabled --------- Co-authored-by: Jonathan Bennett Co-authored-by: Ben Meadors * Don't subscribe to mqtt topic if downlink is not on (#1501) * Dont sub if no downlink * moved reload mqtt connect config * Preview enabled in connected devices (#1509) * Update Muzi R1 Neo to actively supported * Preview enabled in connected devices * Fixing indentation * Fixing indentation --------- Co-authored-by: Jonathan Bennett Co-authored-by: Ben Meadors * UpdateCoreData.updateAnyPacketFrom: mirror firmware's lastHeard/snr/rssi/hopsAway update logic from NodeDB::updateFrom (#1492) * `CLIENT_BASE` add-favorite/role-change confirmation dialog (#1493) * FavoriteNodeButton: refactor task out * AccessoryManager.connectedDeviceRole helper * FavoriteNodeButton: show confirmation dialog when a CLIENT_BASE is trying to add a favorite * addContactFromURL: add comment referencing upcoming change in https://github.com/meshtastic/firmware/pull/8495 * DeviceConfig: role picker: show a warning when selecting CLIENT_BASE, similar to warning shown for ROUTER * Adjust device configuration Client Base warning text * Compass view (#1521) * Added compass view * Added Compass View * Node colors in compass * Update Muzi R1 Neo to actively supported * Update PositionPopover.swift Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update CompassView.swift Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update CompassView.swift Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update Meshtastic/Views/Helpers/CompassView.swift Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update Meshtastic/Views/Helpers/CompassView.swift Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Benjamin Faershtein <119711889+RCGV1@users.noreply.github.com> Co-authored-by: Jonathan Bennett Co-authored-by: Ben Meadors Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Remove discovery queue * revert problematic retry functionalliy * format file * Update & improve zh-Hans translation (#1523) * Update Muzi R1 Neo to actively supported * update & improve zh-Hans translation rt --------- Co-authored-by: Jonathan Bennett Co-authored-by: Ben Meadors * Update protobufs to 2.7.1 * Add long-turbo preset * Disable Range Test module when primary channel is public/unsecured (#1512) * Update Muzi R1 Neo to actively supported * Disable Range Test module when primary channel is public/unsecured Updated RangeTestConfig.swift to determine whether the primary channel (index 0) is operating without encryption or with a 1-byte minimal PSK. Disabled Range Test UI controls when on a public/default channel to prevent user interaction. Added safety enforcement in the save operation: Range Test enabled flag is automatically forced to false before sending updates to the device. Introduced a computed property isPrimaryChannelPublic following existing code patterns and security indicators (e.g., hexDescription PSK length). Matches the behavior implemented in the Android client for consistent policy across platforms. --------- Co-authored-by: Jonathan Bennett Co-authored-by: Ben Meadors * Add new device images * Remove print statement, get rid of cut and paste error --------- Co-authored-by: Gnome Adrift <646322+gnomeadrift@users.noreply.github.com> Co-authored-by: Zain Kergaye <62440012+ZainKergaye@users.noreply.github.com> Co-authored-by: NillRudd <102033730+NillRudd@users.noreply.github.com> Co-authored-by: Jake-B Co-authored-by: Mike Robbins Co-authored-by: Martin Bogomolni Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: jake-b <1012393+jake-b@users.noreply.github.com> Co-authored-by: Benjamin Faershtein <119711889+RCGV1@users.noreply.github.com> Co-authored-by: Jonathan Bennett Co-authored-by: Ben Meadors Co-authored-by: Charles Pinesky <25388414+Vaidios@users.noreply.github.com> Co-authored-by: Radio <35003866+radiolee@users.noreply.github.com> Co-authored-by: Jason Houk --- Localizable.xcstrings | 162 +- Meshtastic.xcodeproj/project.pbxproj | 18 +- .../xcshareddata/swiftpm/Package.resolved | 10 +- .../AccessoryManager+Connect.swift | 20 +- .../AccessoryManager+FromRadio.swift | 14 +- .../AccessoryManager+MQTT.swift | 23 +- .../AccessoryManager+ToRadio.swift | 57 +- .../Accessory Manager/AccessoryManager.swift | 41 +- .../Helpers/ManualConnectionList.swift | 61 + .../Accessory/Protocols/Connection.swift | 2 +- Meshtastic/Accessory/Protocols/Device.swift | 20 +- .../Accessory/Protocols/Transport.swift | 9 +- .../Bluetooth Low Energy/BLETransport.swift | 6 +- .../Transports/Serial/SerialTransport.swift | 13 +- .../Transports/TCP/TCPTransport.swift | 73 +- .../HELTECV4.imageset/Contents.json | 12 + .../HELTECV4.imageset/heltec_v4.svg | 1 + .../MUZIR1NEO.imageset/Contents.json | 12 + .../MUZIR1NEO.imageset/muzi_r1_neo.svg | 141 ++ .../THINKNODEM3.imageset/Contents.json | 12 + .../THINKNODEM3.imageset/thinknode_m3.svg | 1 + .../THINKNODEM4.imageset/Contents.json | 12 + .../THINKNODEM4.imageset/thinknode_m4.svg | 1 + Meshtastic/Enums/LoraConfigEnums.swift | 9 + Meshtastic/Extensions/Bundle.swift | 8 + .../CoreData/ChannelEntityExtension.swift | 37 +- .../CoreData/MessageEntityExtension.swift | 57 +- .../CoreData/MyInfoEntityExtension.swift | 30 +- .../CoreData/PositionEntityExtension.swift | 30 + .../CoreData/UserEntityExtension.swift | 59 +- Meshtastic/Extensions/Url.swift | 12 + Meshtastic/Extensions/UserDefaults.swift | 32 + Meshtastic/Helpers/LocationsHandler.swift | 39 + Meshtastic/Helpers/MeshPackets.swift | 30 +- .../Helpers/Mqtt/MqttClientProxyManager.swift | 16 + .../contents | 4 +- Meshtastic/MeshtasticApp.swift | 120 +- Meshtastic/Persistence/UpdateCoreData.swift | 79 +- Meshtastic/Resources/DeviceHardware.json | 106 +- Meshtastic/Views/Connect/Connect.swift | 196 ++- Meshtastic/Views/Helpers/CompassView.swift | 295 ++++ .../Views/Helpers/ConnectedDevice.swift | 86 +- .../Views/Helpers/RXTXIndicatorView.swift | 8 +- Meshtastic/Views/Messages/ChannelList.swift | 13 +- .../Views/Messages/ChannelMessageList.swift | 27 +- .../Views/Messages/ChannelMessageRow.swift | 4 +- .../Messages/MessageContextMenuItems.swift | 50 +- Meshtastic/Views/Messages/MessageText.swift | 33 +- Meshtastic/Views/Messages/UserList.swift | 26 +- .../Views/Messages/UserMessageList.swift | 53 +- .../Views/Messages/UserMessageRow.swift | 4 +- .../Actions/ExchangePositionsButton.swift | 3 +- .../Helpers/Actions/FavoriteNodeButton.swift | 83 +- .../Map/MapContent/MeshMapContent.swift | 97 +- .../Map/MapContent/NodeMapContent.swift | 146 +- .../Nodes/Helpers/Map/MapSettingsForm.swift | 11 +- .../Nodes/Helpers/Map/NodeMapSwiftUI.swift | 36 +- .../Nodes/Helpers/Map/PositionPopover.swift | 36 +- .../Views/Nodes/Helpers/NodeDetail.swift | 58 +- .../Nodes/Helpers/ShareContactQRDialog.swift | 2 + Meshtastic/Views/Nodes/MeshMap.swift | 3 +- Meshtastic/Views/Nodes/NodeList.swift | 40 +- .../Views/Onboarding/DeviceOnboarding.swift | 2 +- Meshtastic/Views/Settings/AppSettings.swift | 4 + Meshtastic/Views/Settings/Channels.swift | 2 +- .../Views/Settings/Config/DeviceConfig.swift | 24 +- .../Settings/Config/Module/MQTTConfig.swift | 1 + .../Config/Module/RangeTestConfig.swift | 21 +- .../Config/Module/StoreForwardConfig.swift | 2 + .../Views/Settings/SaveChannelQRCode.swift | 12 +- .../Views/Settings/UpdateIntervalPicker.swift | 29 +- MeshtasticProtobufs/Package.swift | 2 +- .../Sources/meshtastic/admin.pb.swift | 516 +----- .../Sources/meshtastic/apponly.pb.swift | 13 +- .../Sources/meshtastic/atak.pb.swift | 137 +- .../meshtastic/cannedmessages.pb.swift | 12 +- .../Sources/meshtastic/channel.pb.swift | 87 +- .../Sources/meshtastic/clientonly.pb.swift | 19 +- .../Sources/meshtastic/config.pb.swift | 809 +++------- .../meshtastic/connection_status.pb.swift | 58 +- .../Sources/meshtastic/device_ui.pb.swift | 186 +-- .../Sources/meshtastic/deviceonly.pb.swift | 107 +- .../Sources/meshtastic/interdevice.pb.swift | 90 +- .../Sources/meshtastic/localonly.pb.swift | 48 +- .../Sources/meshtastic/mesh.pb.swift | 1404 +++++------------ .../Sources/meshtastic/module_config.pb.swift | 533 ++----- .../Sources/meshtastic/mqtt.pb.swift | 34 +- .../Sources/meshtastic/paxcount.pb.swift | 14 +- .../Sources/meshtastic/portnums.pb.swift | 61 +- .../Sources/meshtastic/powermon.pb.swift | 161 +- .../meshtastic/remote_hardware.pb.swift | 52 +- .../Sources/meshtastic/rtttl.pb.swift | 12 +- .../Sources/meshtastic/storeforward.pb.swift | 144 +- .../Sources/meshtastic/telemetry.pb.swift | 296 +--- .../Sources/meshtastic/xmodem.pb.swift | 58 +- protobufs | 2 +- 96 files changed, 3369 insertions(+), 4282 deletions(-) create mode 100644 Meshtastic/Accessory/Helpers/ManualConnectionList.swift create mode 100644 Meshtastic/Assets.xcassets/HELTECV4.imageset/Contents.json create mode 100644 Meshtastic/Assets.xcassets/HELTECV4.imageset/heltec_v4.svg create mode 100644 Meshtastic/Assets.xcassets/MUZIR1NEO.imageset/Contents.json create mode 100644 Meshtastic/Assets.xcassets/MUZIR1NEO.imageset/muzi_r1_neo.svg create mode 100644 Meshtastic/Assets.xcassets/THINKNODEM3.imageset/Contents.json create mode 100644 Meshtastic/Assets.xcassets/THINKNODEM3.imageset/thinknode_m3.svg create mode 100644 Meshtastic/Assets.xcassets/THINKNODEM4.imageset/Contents.json create mode 100644 Meshtastic/Assets.xcassets/THINKNODEM4.imageset/thinknode_m4.svg create mode 100644 Meshtastic/Views/Helpers/CompassView.swift diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 9bbbd0bd..33be032d 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -1486,8 +1486,8 @@ } } }, - "⚠️ The configured value: (%@ seconds) is not one of the optimized options." : { - "comment" : "A text warning that the configured update interval is not one of the optimized options.", + "⚠️ The configured value: (%@) is not one of the optimized options." : { + "comment" : "A warning label below the picker, indicating that the selected update interval is not one of the optimized options.", "isCommentAutoGenerated" : true }, "🦕 End of life Version 🦖 ☄️" : { @@ -3854,7 +3854,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Ambient Lighting module config received: %@" + "value" : "收到环境照明模块配置: %@" } }, "zh-Hant-TW" : { @@ -3905,10 +3905,6 @@ } } }, - "Anonymous Usage and Crash data" : { - "comment" : "A description of how the app collects and uses data about its usage and crashes. It emphasizes that this data is anonymous and non-personally identifiable.", - "isCommentAutoGenerated" : true - }, "Any missed messages will be delivered again." : { "localizations" : { "it" : { @@ -5139,6 +5135,12 @@ } } } + }, + "Bearing: %@" : { + + }, + "Bearing: N/A" : { + }, "Biking" : { "localizations" : { @@ -5459,7 +5461,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Bluetooth config received: %@" + "value" : "收到蓝牙配置: %@" } }, "zh-Hant-TW" : { @@ -6127,7 +6129,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Canned Message module config received: %@" + "value" : "收到预设消息模块配置: %@" } }, "zh-Hant-TW" : { @@ -6313,7 +6315,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Canned Messages Messages Received For: %@" + "value" : "收到预设消息: %@" } }, "zh-Hant-TW" : { @@ -7666,6 +7668,10 @@ } } }, + "Client Base should only favorite other nodes you control. Improper use will hurt your local mesh." : { + "comment" : "A message displayed in a confirmation dialog when trying to favorite a node as a CLIENT_BASE.", + "isCommentAutoGenerated" : true + }, "Client Hidden" : { "localizations" : { "de" : { @@ -8085,6 +8091,9 @@ } } } + }, + "Compass" : { + }, "Config" : { "localizations" : { @@ -10412,7 +10421,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Detection Sensor module config received: %@" + "value" : "收到检测传感器模块配置: %@" } }, "zh-Hant-TW" : { @@ -10638,7 +10647,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Device config received: %@" + "value" : "收到设备配置: %@" } }, "zh-Hant-TW" : { @@ -10822,7 +10831,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Device Metadata admin message received from: %@" + "value" : "已收到来自设备元数据管理员的消息, 来自: %@" } }, "zh-Hant-TW" : { @@ -11795,7 +11804,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Display config received: %@" + "value" : "收到显示屏配置: %@" } }, "zh-Hant-TW" : { @@ -11829,7 +11838,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "展示华氏度" + "value" : "显示华氏度" } }, "zh-Hant-TW" : { @@ -11995,6 +12004,9 @@ } } } + }, + "Distance: %@" : { + }, "Documentation" : { "localizations" : { @@ -12081,7 +12093,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "双击作为按钮" + "value" : "双击代替按钮" } }, "zh-Hant-TW" : { @@ -12417,7 +12429,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "回声" + "value" : "回显" } }, "zh-Hant-TW" : { @@ -13399,7 +13411,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "擦除所有 App 数据?" + "value" : "删除所有 App 数据?" } }, "zh-Hant-TW" : { @@ -13439,7 +13451,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "擦除所有设备和 App 数据?" + "value" : "删除所有设备和 App 数据?" } }, "zh-Hant-TW" : { @@ -14083,7 +14095,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "External Notification module config received: %@" + "value" : "收到外部通知模块配置: %@" } }, "zh-Hant-TW" : { @@ -14700,7 +14712,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Finish" + "value" : "完成" } }, "zh-Hant-TW" : { @@ -14791,7 +14803,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "固件升级文档" + "value" : "固件升级文件" } }, "zh-Hant-TW" : { @@ -16433,7 +16445,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "GPS Receive GPIO" + "value" : "GPS 接收 GPIO" } }, "zh-Hant-TW" : { @@ -16467,7 +16479,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "GPS Transmit GPIO" + "value" : "GPS 发送 GPIO" } }, "zh-Hant-TW" : { @@ -17989,7 +18001,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "如果难以访问设备的重置按钮,请在此进入 DFU 模式。" + "value" : "如果难以触及设备的重置按钮,请在此进入 DFU 模式。" } }, "zh-Hant-TW" : { @@ -18023,7 +18035,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "如果设置了,您发送的任何数据包都会回传到设备。" + "value" : "如果设置,您发送的任何数据包都会回传到设备。" } }, "zh-Hant-TW" : { @@ -18611,7 +18623,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "倒置顶栏,用于双色显示" + "value" : "倒置顶栏,用于双色显示屏" } }, "zh-Hant-TW" : { @@ -18925,6 +18937,13 @@ } } } + }, + "Last seen device:" : { + "comment" : "A label displayed next to the last seen device text in the `DeviceConnectRow`.", + "isCommentAutoGenerated" : true + }, + "Last seen device: %@" : { + }, "Latitude" : { "localizations" : { @@ -20015,7 +20034,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "LoRa config received: %@" + "value" : "收到 LoRa 配置: %@" } }, "zh-Hant-TW" : { @@ -20445,6 +20464,9 @@ } } } + }, + "Manual Connections" : { + }, "Map Data" : { "localizations" : { @@ -20929,6 +20951,9 @@ } } } + }, + "Meshtastic does not collect any personal information. We do anonymously collect usage and crash data to improve the app. You can opt out under app settings." : { + }, "Meshtastic Node %@ has shared channels with you" : { "localizations" : { @@ -21193,7 +21218,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Message received from the text message app." + "value" : "收到来自短信应用的消息。" } }, "zh-Hant-TW" : { @@ -22196,7 +22221,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "MyInfo received: %@" + "value" : "收到我的消息: %@" } }, "zh-Hant-TW" : { @@ -22556,7 +22581,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Network config received: %@" + "value" : "收到网络配置: %@" } }, "zh-Hant-TW" : { @@ -24754,6 +24779,9 @@ } } } + }, + "Open Compass" : { + }, "Open Settings" : { "localizations" : { @@ -25742,7 +25770,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "PAX Counter message received for: %@" + "value" : "收到 PAX 计数信息: %@" } }, "zh-Hant-TW" : { @@ -27464,7 +27492,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Process" + "value" : "处理" } }, "zh-Hant-TW" : { @@ -28470,6 +28498,7 @@ } }, "Received Ack" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -28532,8 +28561,12 @@ } } } + }, + "Received Ack: %@" : { + }, "Recipient Ack" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -28596,6 +28629,9 @@ } } } + }, + "Recipient Ack: %@" : { + }, "Recording route" : { "localizations" : { @@ -28779,6 +28815,16 @@ } } }, + "Relayed by %d %@" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Relayed by %1$d %2$@" + } + } + } + }, "Release Notes" : { "localizations" : { "it" : { @@ -29228,7 +29274,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Requested Canned Messages Module Messages for node: %@" + "value" : "请求的节点预设消息模块消息: %@" } }, "zh-Hant-TW" : { @@ -30356,7 +30402,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Routing received for RequestID: %@ Ack Status: %@" + "value" : "收到请求ID为 %@ 的路由, Ack 状态: %@" } }, "zh-Hant-TW" : { @@ -30504,7 +30550,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "RTTTL Ringtone config received: %@" + "value" : "收到 RTTTL 铃声配置: %@" } }, "zh-Hant-TW" : { @@ -32306,7 +32352,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Sent a Channel for: %@ Channel Index %d" + "value" : "已发送频道: %@ 频道索引 %d" } }, "zh-Hant-TW" : { @@ -32370,7 +32416,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Sent a LoRa.Config for: %@" + "value" : "发送 LoRa.Config 给: %@" } }, "zh-Hant-TW" : { @@ -32435,7 +32481,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Sent a Position Packet from the Apple device GPS to node: %@" + "value" : "从苹果设备 GPS 发送定位数据包给节点: %@" } }, "zh-Hant-TW" : { @@ -32499,7 +32545,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Sent a Trace Route Request to node: %@" + "value" : "发送跟踪路由请求到节点: %@" } }, "zh-Hant-TW" : { @@ -32563,7 +32609,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Sent a Waypoint Packet from: %@" + "value" : "发送来自的航点数据包: %@" } }, "zh-Hant-TW" : { @@ -32627,7 +32673,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Sent message %@ from %@ to %@" + "value" : "发送消息 %@ 来自 %@ 给 %@" } }, "zh-Hant-TW" : { @@ -32949,7 +32995,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Serial module config received: %@" + "value" : "收到串口模块配置: %@" } }, "zh-Hant-TW" : { @@ -35131,7 +35177,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Store & Forward module config received: %@" + "value" : "收到存储和转发模块配置: %@" } }, "zh-Hant-TW" : { @@ -35750,7 +35796,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Telemetry module config received: %@" + "value" : "收到遥测模块配置: %@" } }, "zh-Hant-TW" : { @@ -36743,7 +36789,12 @@ } } }, + "These settings will %@" : { + "comment" : "A paragraph below the title that explains what the user is about to do.", + "isCommentAutoGenerated" : true + }, "These settings will %@ channels. The current LoRa Config will be replaced, if there are substantial changes to the LoRa config the device will reboot" : { + "extractionState" : "stale", "localizations" : { "it" : { "stringUnit" : { @@ -38000,7 +38051,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Trace Route request returned: %@" + "value" : "跟踪路由请求回复: %@" } }, "zh-Hant-TW" : { @@ -40176,6 +40227,9 @@ } } } + }, + "User Privacy" : { + }, "User Uploaded" : { "comment" : "Data source label for user uploaded files", @@ -41027,7 +41081,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Waypoint Packet received from node: %@" + "value" : "收到航点数据包, 来自节点: %@" } }, "zh-Hant-TW" : { @@ -41040,10 +41094,6 @@ }, "Waypoints" : { - }, - "We anonymously collect usage and crash data to improve the app. This helps us understand how the app is being used and where we can make improvements. The data we collect is non-personally identifiable and cannot be linked to you as an individual. You can opt out of this under app settings." : { - "comment" : "A description of how the app collects and uses user data. Includes a link to the app settings.", - "isCommentAutoGenerated" : true }, "Weather Conditions" : { "localizations" : { @@ -41220,7 +41270,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "锁意味着什么?" + "value" : "锁头图标含义" } }, "zh-Hant-TW" : { @@ -41260,7 +41310,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "什么是 Meshtastic?" + "value" : "Meshtastic 是什么?" } }, "zh-Hant-TW" : { @@ -41854,6 +41904,10 @@ } } }, + "Yes, I control this node" : { + "comment" : "A button label that appears in a confirmation sheet when favoriting a node as a CLIENT_BASE.", + "isCommentAutoGenerated" : true + }, "Yesterday" : { "localizations" : { "de" : { @@ -42267,4 +42321,4 @@ } }, "version" : "1.1" -} \ No newline at end of file +} diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 63f334e5..b87ce5f2 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -42,6 +42,7 @@ 2344A2B12D68DFF800170A77 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25C49D8F2C471AEA0024FBD1 /* Constants.swift */; }; 2346A7192E2FB9A300CB9239 /* SerialConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2346A7182E2FB9A300CB9239 /* SerialConnection.swift */; }; 2346A71D2E2FB9C500CB9239 /* SerialTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2346A71C2E2FB9C500CB9239 /* SerialTransport.swift */; }; + 2349A04A2EAE4DA30060A581 /* ManualConnectionList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2349A0492EAE4DA30060A581 /* ManualConnectionList.swift */; }; 2373AE132D0A216C0086C749 /* MetricsChartSeries.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2373AE122D0A216C0086C749 /* MetricsChartSeries.swift */; }; 2373AE152D0A24930086C749 /* MetricsSeriesList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2373AE142D0A24930086C749 /* MetricsSeriesList.swift */; }; 2373AE172D0A26620086C749 /* EnvironmentDefaultSeries.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2373AE162D0A26620086C749 /* EnvironmentDefaultSeries.swift */; }; @@ -97,6 +98,7 @@ B3E905B12B71F7F300654D07 /* TextMessageField.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E905B02B71F7F300654D07 /* TextMessageField.swift */; }; BC10380F2DD4334400B00BFA /* AddContactIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC10380E2DD4333C00B00BFA /* AddContactIntent.swift */; }; BC6B45FF2CB2F98900723CEB /* SaveChannelSettingsIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC6B45FE2CB2F98900723CEB /* SaveChannelSettingsIntent.swift */; }; + BCA9A82C2EC802CF00166292 /* CompassView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA9A82B2EC802CF00166292 /* CompassView.swift */; }; BCB35B4F2E5FC42500B04F60 /* MessageNodeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB35B4E2E5FC41E00B04F60 /* MessageNodeIntent.swift */; }; BCB613812C67290800485544 /* SendWaypointIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB613802C67290800485544 /* SendWaypointIntent.swift */; }; BCB613832C672A2600485544 /* MessageChannelIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB613822C672A2600485544 /* MessageChannelIntent.swift */; }; @@ -357,6 +359,7 @@ 2344A2AE2D6697A700170A77 /* TelemetryEntity+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TelemetryEntity+CoreDataProperties.swift"; sourceTree = ""; }; 2346A7182E2FB9A300CB9239 /* SerialConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialConnection.swift; sourceTree = ""; }; 2346A71C2E2FB9C500CB9239 /* SerialTransport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialTransport.swift; sourceTree = ""; }; + 2349A0492EAE4DA30060A581 /* ManualConnectionList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManualConnectionList.swift; sourceTree = ""; }; 2373AE122D0A216C0086C749 /* MetricsChartSeries.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetricsChartSeries.swift; sourceTree = ""; }; 2373AE142D0A24930086C749 /* MetricsSeriesList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetricsSeriesList.swift; sourceTree = ""; }; 2373AE162D0A26620086C749 /* EnvironmentDefaultSeries.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentDefaultSeries.swift; sourceTree = ""; }; @@ -407,6 +410,7 @@ B3E905B02B71F7F300654D07 /* TextMessageField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextMessageField.swift; sourceTree = ""; }; BC10380E2DD4333C00B00BFA /* AddContactIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactIntent.swift; sourceTree = ""; }; BC6B45FE2CB2F98900723CEB /* SaveChannelSettingsIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveChannelSettingsIntent.swift; sourceTree = ""; }; + BCA9A82B2EC802CF00166292 /* CompassView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompassView.swift; sourceTree = ""; }; BCB35B4E2E5FC41E00B04F60 /* MessageNodeIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageNodeIntent.swift; sourceTree = ""; }; BCB613802C67290800485544 /* SendWaypointIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendWaypointIntent.swift; sourceTree = ""; }; BCB613822C672A2600485544 /* MessageChannelIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageChannelIntent.swift; sourceTree = ""; }; @@ -832,6 +836,7 @@ 23D316922E5618D2002FA4FB /* AsyncGate.swift */, 23E23F912E392C2B00919073 /* LogRecord+StringRepresentation.swift */, 23D9D9382E50DA97005D1C18 /* ResettableTimer.swift */, + 2349A0492EAE4DA30060A581 /* ManualConnectionList.swift */, ); path = Helpers; sourceTree = ""; @@ -1271,6 +1276,7 @@ 8D3F8A3E2D44BB02009EAAA4 /* PowerMetrics.swift */, 237B46952DC8F1C100B22D99 /* RateLimitedButton.swift */, 23A1AFB62E42BD2500E46C96 /* RXTXIndicatorView.swift */, + BCA9A82B2EC802CF00166292 /* CompassView.swift */, ); path = Helpers; sourceTree = ""; @@ -1811,6 +1817,7 @@ 233E99BE2D849D3200CC3A77 /* RadiationCompactWidget.swift in Sources */, DDD94A502845C8F5004A87A0 /* DateTimeText.swift in Sources */, DDB6ABE228B13FB500384BA1 /* PositionConfigEnums.swift in Sources */, + BCA9A82C2EC802CF00166292 /* CompassView.swift in Sources */, DD994B69295F88B60013760A /* IntervalEnums.swift in Sources */, 23D316932E5618D2002FA4FB /* AsyncGate.swift in Sources */, 23FF00B62E323C75001DF095 /* AccessoryManager+Connect.swift in Sources */, @@ -1841,6 +1848,7 @@ BCDDFA9A2DBB180D0065189C /* ScrollToBottomButton.swift in Sources */, DDC4C9FF2A8D982900CE201C /* DetectionSensorConfig.swift in Sources */, 2344A2AF2D6697A700170A77 /* TelemetryEntity+CoreDataClass.swift in Sources */, + 2349A04A2EAE4DA30060A581 /* ManualConnectionList.swift in Sources */, 2344A2B02D6697A700170A77 /* TelemetryEntity+CoreDataProperties.swift in Sources */, D9C983A22B79D1A600BDBE6A /* RequestPositionButton.swift in Sources */, 237AEB8F2E1FE457003B7CE3 /* Transport.swift in Sources */, @@ -2094,7 +2102,7 @@ "@executable_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 14.6; - MARKETING_VERSION = 2.7.5; + MARKETING_VERSION = 2.7.6; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -2129,7 +2137,7 @@ "@executable_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 14.6; - MARKETING_VERSION = 2.7.5; + MARKETING_VERSION = 2.7.6; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -2161,7 +2169,7 @@ "@executable_path/../../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 14.6; - MARKETING_VERSION = 2.7.5; + MARKETING_VERSION = 2.7.6; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2194,7 +2202,7 @@ "@executable_path/../../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 14.6; - MARKETING_VERSION = 2.7.5; + MARKETING_VERSION = 2.7.6; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2261,7 +2269,7 @@ requirement = { kind = versionRange; maximumVersion = 4.0.0; - minimumVersion = 3.1.0; + minimumVersion = 3.3.0; }; }; 259792242C2F10B600AD1659 /* XCRemoteSwiftPackageReference "swift-protobuf" */ = { diff --git a/Meshtastic.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Meshtastic.xcworkspace/xcshareddata/swiftpm/Package.resolved index 84f5fd5e..ba8776f7 100644 --- a/Meshtastic.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Meshtastic.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "fd71b247ba909b0eb360db5530e1068363839c5e169dea6f6a9974b2d98276f4", + "originHash" : "25240dd07109fa832be10093f5d97529f872f18e8d9df6468e5e4212bc0b487e", "pins" : [ { "identity" : "cocoamqtt", @@ -15,8 +15,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DataDog/dd-sdk-ios.git", "state" : { - "revision" : "a193cab2786b704ebc98c290a7cc8a3165f636bb", - "version" : "3.1.0" + "revision" : "8d67e973ff4a958cb536263cb816646ee904c508", + "version" : "3.3.0" } }, { @@ -60,8 +60,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-protobuf.git", "state" : { - "revision" : "d72aed98f8253ec1aa9ea1141e28150f408cf17f", - "version" : "1.29.0" + "revision" : "c169a5744230951031770e27e475ff6eefe51f9d", + "version" : "1.33.3" } } ], diff --git a/Meshtastic/Accessory/Accessory Manager/AccessoryManager+Connect.swift b/Meshtastic/Accessory/Accessory Manager/AccessoryManager+Connect.swift index df627fbe..d5ca0929 100644 --- a/Meshtastic/Accessory/Accessory Manager/AccessoryManager+Connect.swift +++ b/Meshtastic/Accessory/Accessory Manager/AccessoryManager+Connect.swift @@ -66,10 +66,6 @@ extension AccessoryManager { Logger.transport.info("[Accessory] Event stream closed") } self.activeConnection = (device: device, connection: connection) - - if UserDefaults.preferredPeripheralId.count < 1 { - UserDefaults.preferredPeripheralId = device.id.uuidString - } } catch let error as CBError where error.code == .peerRemovedPairingInformation { await self.connectionStepper?.cancelCurrentlyExecutingStep(withError: AccessoryError.coreBluetoothError(error), cancelFullProcess: true) } @@ -114,6 +110,10 @@ extension AccessoryManager { Logger.transport.info("🔗👟 [Connect] Step 5: Send wantConfig (database)") self.updateState(.retrievingDatabase(nodeCount: 0)) self.allowDisconnect = true + + Logger.transport.info("🔗 Saving preferredPeripheralId: \(device.id.uuidString)") + UserDefaults.preferredPeripheralId = device.id.uuidString + try await self.sendWantDatabase() } @@ -168,6 +168,15 @@ extension AccessoryManager { // We have an active connection self.updateDevice(deviceId: device.id, key: \.connectionState, value: .connected) self.updateState(.subscribed) + + // If we successfully connected to a manual connection, then save it to the list + // Remember, Device is a value type (struct) so don't use use `device` here, thats + // The value at the instantiation of the connect process. We want the currently + // updated device object in `activeConnection` with its additonal metadata from + // NodeInfo packets. + if let activeDevice = self.activeConnection?.device, activeDevice.isManualConnection { + ManualConnectionList.shared.insert(device: activeDevice) + } } // Step 8: Update UI and status to connected @@ -258,7 +267,7 @@ actor SequentialSteps { var isRunning: Bool = false var externalError: Error? - init(maxRetries: Int = 1, retryDelay: Duration = .seconds(3), @StepsBuilder _ builder: () -> [Step]) { + init(maxRetries: Int = 3, retryDelay: Duration = .seconds(3), @StepsBuilder _ builder: () -> [Step]) { self.maxRetries = maxRetries self.retryDelay = retryDelay self.steps = builder() @@ -343,6 +352,7 @@ actor SequentialSteps { return } isRunning = false + return throw AccessoryError.tooManyRetries } diff --git a/Meshtastic/Accessory/Accessory Manager/AccessoryManager+FromRadio.swift b/Meshtastic/Accessory/Accessory Manager/AccessoryManager+FromRadio.swift index 01cdac79..5bcead9b 100644 --- a/Meshtastic/Accessory/Accessory Manager/AccessoryManager+FromRadio.swift +++ b/Meshtastic/Accessory/Accessory Manager/AccessoryManager+FromRadio.swift @@ -106,13 +106,25 @@ extension AccessoryManager { return } + // Check if we're in database retrieval mode to defer saves for performance + let isRetrievingDatabase = if case .retrievingDatabase = self.state { true } else { false } + // TODO: nodeInfoPacket's channel: parameter is not used - if let nodeInfo = nodeInfoPacket(nodeInfo: nodeInfo, channel: 0, context: context) { + if let nodeInfo = nodeInfoPacket(nodeInfo: nodeInfo, channel: 0, context: context, deferSave: isRetrievingDatabase) { if let activeDevice = activeConnection?.device, activeDevice.num == nodeInfo.num { if let user = nodeInfo.user { updateDevice(deviceId: activeDevice.id, key: \.shortName, value: user.shortName ?? "?") updateDevice(deviceId: activeDevice.id, key: \.longName, value: user.longName ?? "Unknown".localized) updateDevice(deviceId: activeDevice.id, key: \.hardwareModel, value: user.hwModel) + + if activeDevice.isManualConnection { + // We just received a NodeInfo for the currently connected node and this is a + // manual connection. Update the metadata for the device entry in UserDefaults + // with this information for better display later + ManualConnectionList.shared.updateDevice(deviceId: activeDevice.id, key: \.shortName, value: user.shortName) + ManualConnectionList.shared.updateDevice(deviceId: activeDevice.id, key: \.longName, value: user.longName) + ManualConnectionList.shared.updateDevice(deviceId: activeDevice.id, key: \.hardwareModel, value: user.hwModel) + } } } } diff --git a/Meshtastic/Accessory/Accessory Manager/AccessoryManager+MQTT.swift b/Meshtastic/Accessory/Accessory Manager/AccessoryManager+MQTT.swift index 7c286336..4da8c739 100644 --- a/Meshtastic/Accessory/Accessory Manager/AccessoryManager+MQTT.swift +++ b/Meshtastic/Accessory/Accessory Manager/AccessoryManager+MQTT.swift @@ -32,14 +32,12 @@ extension AccessoryManager { } } // Set initial unread message badge states - appState.unreadChannelMessages = fetchedNodeInfo[0].myInfo?.unreadMessages ?? 0 - appState.unreadDirectMessages = fetchedNodeInfo[0].user?.unreadMessages ?? 0 - } - if fetchedNodeInfo.count == 1 && fetchedNodeInfo[0].rangeTestConfig?.enabled == true { - wantRangeTestPackets = true - } - if fetchedNodeInfo.count == 1 && fetchedNodeInfo[0].storeForwardConfig?.enabled == true { - wantStoreAndForwardPackets = true + appState.unreadChannelMessages = fetchedNodeInfo[0].myInfo?.unreadMessages(context: context) ?? 0 + appState.unreadDirectMessages = fetchedNodeInfo[0].user?.unreadMessages(context: context, skipLastMessageCheck: true) ?? 0 // skipLastMessageCheck=true because we don't update lastMessage on our own connected node + + // Set wantRangeTestPackets and wantStoreAndForwardPackets + wantRangeTestPackets = fetchedNodeInfo[0].rangeTestConfig?.enabled ?? false + wantStoreAndForwardPackets = fetchedNodeInfo[0].storeForwardConfig?.enabled ?? false } } catch { Logger.data.error("Failed to find a node info for the connected node \(error.localizedDescription, privacy: .public)") @@ -51,8 +49,12 @@ extension AccessoryManager { func onMqttConnected() { mqttProxyConnected = true mqttError = "" - Logger.services.info("📲 [MQTT Client Proxy] onMqttConnected now subscribing to \(self.mqttManager.topic, privacy: .public).") - mqttManager.mqttClientProxy?.subscribe(mqttManager.topic) + if mqttManager.shouldSubscribe { + Logger.services.info("📲 [MQTT Client Proxy] onMqttConnected now subscribing to \(self.mqttManager.topic, privacy: .public).") + mqttManager.mqttClientProxy?.subscribe(mqttManager.topic) + } else { + Logger.services.info("📲 [MQTT Client Proxy] onMqttConnected not subscribing since downlink is not on") + } } func onMqttDisconnected() { @@ -83,3 +85,4 @@ extension AccessoryManager { Logger.services.info("📲 [MQTT Client Proxy] onMqttError: \(message, privacy: .public)") } } + diff --git a/Meshtastic/Accessory/Accessory Manager/AccessoryManager+ToRadio.swift b/Meshtastic/Accessory/Accessory Manager/AccessoryManager+ToRadio.swift index e0478902..4fe2ffaf 100644 --- a/Meshtastic/Accessory/Accessory Manager/AccessoryManager+ToRadio.swift +++ b/Meshtastic/Accessory/Accessory Manager/AccessoryManager+ToRadio.swift @@ -165,6 +165,7 @@ extension AccessoryManager { nodeMeshPacket.decoded = dataNodeMessage // Update local database with the new node info + // FUTURE: after https://github.com/meshtastic/firmware/pull/8495 is merged, `favorite: true` becomes `favorite: (connectedDeviceRole != DeviceRoles.clientBase)` upsertNodeInfoPacket(packet: nodeMeshPacket, favorite: true, context: context) } } catch { @@ -333,7 +334,7 @@ extension AccessoryManager { var contact = SharedContact() contact.manuallyVerified = false contact.nodeNum = UInt32(truncatingIfNeeded: user.num) - user.userNode?.favorite = true + user.userNode?.favorite = user.userNode?.deviceConfig?.role ?? 0 != DeviceRoles.clientBase.rawValue contact.user = user.toProto() do { let contactString = try contact.serializedData().base64EncodedString() @@ -350,7 +351,7 @@ extension AccessoryManager { if toUserNum > 0 { meshPacket.to = UInt32(toUserNum) let hopsAway = newMessage.toUser?.userNode?.hopsAway ?? 0 - if hopsAway > Int32(truncatingIfNeeded: newMessage.toUser?.userNode?.loRaConfig?.hopLimit ?? 0) { + if hopsAway > Int32(truncatingIfNeeded: newMessage.fromUser?.userNode?.loRaConfig?.hopLimit ?? 0) { meshPacket.hopLimit = UInt32(truncatingIfNeeded: hopsAway) } } else { @@ -509,32 +510,32 @@ extension AccessoryManager { let logString = String.localizedStringWithFormat("Sent a Channel for: %@ Channel Index %d".localized, String(deviceNum), chan.index) try await send(toRadio, debugDescription: logString) } - - // Save the LoRa Config and the device will reboot - var adminPacket = AdminMessage() - adminPacket.setConfig.lora = channelSet.loraConfig - adminPacket.setConfig.lora.configOkToMqtt = okToMQTT // Preserve users okToMQTT choice - var meshPacket: MeshPacket = MeshPacket() - meshPacket.to = UInt32(deviceNum) - meshPacket.from = UInt32(deviceNum) - meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. with nil deviceId") return } - - // Update the active device - if let activeConnection { + + // Update the active device if the UUID's match + if let activeConnection, activeConnection.device.id == deviceId { var device = activeConnection.device if device[keyPath: key] != value { // Update the @Published stuff for the UI @@ -493,6 +493,14 @@ class AccessoryManager: ObservableObject, MqttClientProxyManagerDelegate { handleMyInfo(myNodeInfo) case .packet(let packet): + // All received packets get passed through updateAnyPacketFrom to update lastHeard, rxSnr, etc. (like firmware's NodeDB::updateFrom). + if let connectedNodeNum = self.activeDeviceNum { + updateAnyPacketFrom(packet: packet, activeDeviceNum: connectedNodeNum, context: context) + } else { + Logger.mesh.error("🕸️ Unable to determine connectedNodeNum for updateAnyPacketFrom. Skipping.") + } + + // Dispatch based on packet contents. if case let .decoded(data) = packet.payloadVariant { switch data.portnum { case .textMessageApp, .detectionSensorApp, .alertApp: @@ -570,6 +578,8 @@ class AccessoryManager: ObservableObject, MqttClientProxyManagerDelegate { Logger.mesh.info("🕸️ MESH PACKET received for ATAK Forwarder App UNHANDLED UNHANDLED") case .simulatorApp: Logger.mesh.info("🕸️ MESH PACKET received for Simulator App UNHANDLED UNHANDLED") + case .storeForwardPlusplusApp: + Logger.mesh.info("🕸️ MESH PACKET received for SFPP App UNHANDLED UNHANDLED") case .audioApp: Logger.mesh.info("🕸️ MESH PACKET received for Audio App UNHANDLED UNHANDLED") case .tracerouteApp: @@ -665,6 +675,17 @@ class AccessoryManager: ObservableObject, MqttClientProxyManagerDelegate { self.firstDatabaseNodeInfoContinuation = nil } + // Perform a single batch save after database retrieval completes + // This significantly improves performance on reconnect + do { + try context.save() + Logger.data.info("💾 [Database] Batch saved all node info after database retrieval") + } catch { + context.rollback() + let nsError = error as NSError + Logger.data.error("💥 [Database] Error saving batch node info: \(nsError, privacy: .public)") + } + default: Logger.transport.error("[Accessory] Unknown nonce completed: \(configCompleteID)") } @@ -687,6 +708,13 @@ extension AccessoryManager { return activeConnection?.device.firmwareVersion } + var connectedDeviceRole: DeviceRoles? { + guard let connectedNodeNum = activeDeviceNum else { return nil } + guard let connectedNode = getNodeInfo(id: connectedNodeNum, context: context) else { return nil } + guard let connectedNodeUser = connectedNode.user else { return nil } + return DeviceRoles(rawValue: Int(connectedNodeUser.role)) + } + func checkIsVersionSupported(forVersion: String) -> Bool { let myVersion = connectedVersion ?? "0.0.0" let supportedVersion = UserDefaults.firmwareVersion == "0.0.0" || @@ -703,7 +731,8 @@ extension AccessoryManager { await self.heartbeatTimer?.cancel(withReason: "Duplicate setup, cancelling previous timer") self.heartbeatTimer = nil } - self.heartbeatTimer = ResettableTimer(isRepeating: true, debugName: "Send Heartbeat") { + + self.heartbeatTimer = ResettableTimer(isRepeating: true, debugName: Bundle.main.isDebug ? "Send Heartbeat" : nil) { Logger.transport.debug("💓 [Heartbeat] Sending periodic heartbeat") try? await self.sendHeartbeat() } @@ -711,7 +740,7 @@ extension AccessoryManager { // We can send heartbeats for older versions just fine, but only 2.7.4 and up will respond with // a definite queueStatus packet. if self.checkIsVersionSupported(forVersion: "2.7.4") { - self.heartbeatResponseTimer = ResettableTimer(isRepeating: false, debugName: "Heartbeat Timeout") { @MainActor in + self.heartbeatResponseTimer = ResettableTimer(isRepeating: false, debugName: Bundle.main.isDebug ? "Heartbeat Timeout" : nil) { @MainActor in Logger.transport.error("💓 [Heartbeat] Connection Timeout: Did not receive a packet after heartbeat.") // If we're in the middle of a connection cancel it. await self.connectionStepper?.cancel() diff --git a/Meshtastic/Accessory/Helpers/ManualConnectionList.swift b/Meshtastic/Accessory/Helpers/ManualConnectionList.swift new file mode 100644 index 00000000..665573a9 --- /dev/null +++ b/Meshtastic/Accessory/Helpers/ManualConnectionList.swift @@ -0,0 +1,61 @@ +// +// ManualConnectionList.swift +// Meshtastic +// +// Created by jake on 10/26/25. +// + +import Foundation + +// Maintains an observable list of devices that's backed by UserDefaults +public class ManualConnectionList: ObservableObject { + static let shared = ManualConnectionList() + + @Published private var _list: [Device] + + private init() { + _list = UserDefaults.manualConnections + } + + var connectionsList: [Device] { + get { + return _list + } + } + + func insert(device: Device) { + // Don't insert if already there + guard !_list.contains(where: {$0.id == device.id}) else { + return + } + + // Add the new entry + var list = _list + list.append(device) + _list = list + UserDefaults.manualConnections = list + } + + func updateDevice(deviceId: UUID, key: WritableKeyPath, value: T) where T: Equatable { + var list = _list + if let deviceIndex = list.firstIndex(where: {$0.id == deviceId}) { + list[deviceIndex][keyPath: key] = value + _list = list + UserDefaults.manualConnections = list + } + } + + func remove(device: Device) { + var list = _list + list.removeAll(where: {$0.id == device.id}) + _list = list + UserDefaults.manualConnections = list + } + + func remove(atOffsets: IndexSet) { + var list = _list + list.remove(atOffsets: atOffsets) + _list = list + UserDefaults.manualConnections = list + } +} diff --git a/Meshtastic/Accessory/Protocols/Connection.swift b/Meshtastic/Accessory/Protocols/Connection.swift index afc087c5..27758b84 100644 --- a/Meshtastic/Accessory/Protocols/Connection.swift +++ b/Meshtastic/Accessory/Protocols/Connection.swift @@ -31,7 +31,7 @@ enum ConnectionEvent { case disconnected(shouldReconnect: Bool) } -enum ConnectionState: Equatable { +enum ConnectionState: Equatable, Codable { case disconnected case connecting case connected diff --git a/Meshtastic/Accessory/Protocols/Device.swift b/Meshtastic/Accessory/Protocols/Device.swift index 03ae1d1d..d14b349e 100644 --- a/Meshtastic/Accessory/Protocols/Device.swift +++ b/Meshtastic/Accessory/Protocols/Device.swift @@ -7,7 +7,8 @@ import Foundation -struct Device: Identifiable, Hashable { +struct Device: Identifiable, Hashable, Codable, CustomStringConvertible { + let id: UUID var name: String var transportType: TransportType @@ -23,7 +24,9 @@ struct Device: Identifiable, Hashable { var connectionState: ConnectionState var wasRestored: Bool = false - init(id: UUID, name: String, transportType: TransportType, identifier: String, connectionState: ConnectionState = .disconnected, rssi: Int? = nil, num: Int64? = nil, wasRestored: Bool = false) { + var isManualConnection: Bool = false + + init(id: UUID, name: String, transportType: TransportType, identifier: String, connectionState: ConnectionState = .disconnected, rssi: Int? = nil, num: Int64? = nil, wasRestored: Bool = false, isManualConnection: Bool = false) { self.id = id self.name = name self.transportType = transportType @@ -32,6 +35,7 @@ struct Device: Identifiable, Hashable { self.rssi = rssi self.num = num self.wasRestored = wasRestored + self.isManualConnection = isManualConnection } var rssiString: String { @@ -53,4 +57,16 @@ struct Device: Identifiable, Hashable { } } + var description: String { + switch (shortName, longName) { + case (let shortName?, let longName?): // Both shortName and longName are non-nil + return "\(longName) (\(shortName))" + case (let shortName?, nil): // shortName is non-nil, longName is nil + return "\(shortName)" + case (nil, let longName?): // shortName is nil, longName is non-nil + return "\(longName)" + default: // Both are nil + return "Device(id: \(id))" + } + } } diff --git a/Meshtastic/Accessory/Protocols/Transport.swift b/Meshtastic/Accessory/Protocols/Transport.swift index 0c5cce08..55fa8545 100644 --- a/Meshtastic/Accessory/Protocols/Transport.swift +++ b/Meshtastic/Accessory/Protocols/Transport.swift @@ -9,7 +9,7 @@ import Foundation import CommonCrypto import SwiftUI -enum TransportType: String, CaseIterable { +enum TransportType: String, CaseIterable, Codable { case ble = "BLE" case tcp = "TCP" case serial = "Serial" @@ -53,14 +53,15 @@ protocol Transport { var requiresPeriodicHeartbeat: Bool { get } var supportsManualConnection: Bool { get } - func manuallyConnect(withConnectionString: String) async throws + func device(forManualConnection: String) -> Device? + func manuallyConnect(toDevice: Device) async throws } // Used to make stable-ish ID's for accessories that don't have a UUID extension String { - func toUUIDFormatHash() -> UUID? { + func toUUIDFormatHash() -> UUID { // Convert string to data - guard let data = self.data(using: .utf8) else { return nil } + let data = self.data(using: .utf8) ?? Data() // Create buffer for SHA-256 hash (32 bytes) var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) diff --git a/Meshtastic/Accessory/Transports/Bluetooth Low Energy/BLETransport.swift b/Meshtastic/Accessory/Transports/Bluetooth Low Energy/BLETransport.swift index ea4a22b8..aa1a32d4 100644 --- a/Meshtastic/Accessory/Transports/Bluetooth Low Energy/BLETransport.swift +++ b/Meshtastic/Accessory/Transports/Bluetooth Low Energy/BLETransport.swift @@ -403,7 +403,11 @@ class BLETransport: Transport { } - func manuallyConnect(withConnectionString: String) async throws { + func device(forManualConnection: String) -> Device? { + return nil + } + + func manuallyConnect(toDevice: Device) async throws { Logger.transport.error("🛜 [BLE] This transport does not support manual connections") } diff --git a/Meshtastic/Accessory/Transports/Serial/SerialTransport.swift b/Meshtastic/Accessory/Transports/Serial/SerialTransport.swift index 920dd538..b34b1884 100644 --- a/Meshtastic/Accessory/Transports/Serial/SerialTransport.swift +++ b/Meshtastic/Accessory/Transports/Serial/SerialTransport.swift @@ -31,7 +31,7 @@ class SerialTransport: Transport { while !Task.isCancelled { let ports = self.getSerialPorts() for port in ports { - let id = port.toUUIDFormatHash() ?? UUID() + let id = port.toUUIDFormatHash() if !portsAlreadyNotified.contains(port) { Logger.transport.info("🔱 [Serial] Port \(port, privacy: .public) found.") let newDevice = Device(id: id, @@ -45,9 +45,8 @@ class SerialTransport: Transport { for knownPort in portsAlreadyNotified where !ports.contains(knownPort) { // Previosuly seen port is no longer available Logger.transport.info("🔱 [Serial] Port \(knownPort, privacy: .public) is no longer connected.") - if let uuid = knownPort.toUUIDFormatHash() { - cont.yield(.deviceLost(uuid)) - } + let uuid = knownPort.toUUIDFormatHash() + cont.yield(.deviceLost(uuid)) portsAlreadyNotified.removeAll(where: {$0 == knownPort}) } try? await Task.sleep(for: .seconds(5)) @@ -118,7 +117,11 @@ class SerialTransport: Transport { return SerialConnection(path: device.identifier) } - func manuallyConnect(withConnectionString: String) async throws { + func device(forManualConnection: String) -> Device? { + return nil + } + + func manuallyConnect(toDevice: Device) async throws { Logger.transport.error("🔱 [USB] This transport does not support manual connections") } } diff --git a/Meshtastic/Accessory/Transports/TCP/TCPTransport.swift b/Meshtastic/Accessory/Transports/TCP/TCPTransport.swift index d753fc76..1acc988c 100644 --- a/Meshtastic/Accessory/Transports/TCP/TCPTransport.swift +++ b/Meshtastic/Accessory/Transports/TCP/TCPTransport.swift @@ -73,13 +73,29 @@ class TCPTransport: NSObject, Transport, NetServiceBrowserDelegate, NetServiceDe let ip = service.ipv4Address ?? "Unknown IP" // Use a mishmash of things and hash for stable? ID. - let idString = "\(service.name):\(host):\(ip):\(port)".toUUIDFormatHash() ?? UUID() + let idString = "\(service.name):\(host):\(ip):\(port)".toUUIDFormatHash() // Save the resolved service locally for later services[service.name] = ResolvedService(id: idString, service: service, host: host, port: port) + let name: String + if let txtRecords = service.txtRecordData().map({NetService.dictionary(fromTXTRecord: $0)}) { + var nodeNameString = "" + if let shortNameData = txtRecords["shortname"] { + nodeNameString += String(decoding: shortNameData, as: UTF8.self) + } + if let nodeId = txtRecords["id"], nodeId.count > 4 { + if nodeNameString.count > 0 { + nodeNameString += "_" + } + nodeNameString += String(decoding: nodeId.suffix(4), as: UTF8.self) + } + name = nodeNameString + } else { + name = "\(service.name) (\(ip))" + } let device = Device(id: idString, - name: "\(service.name) (\(ip))", + name: name, transportType: .tcp, identifier: "\(host):\(port)") continuation?.yield(.deviceFound(device)) @@ -134,16 +150,57 @@ class TCPTransport: NSObject, Transport, NetServiceBrowserDelegate, NetServiceDe } } - func manuallyConnect(withConnectionString: String) async throws { - let hashedIdentifier = withConnectionString.toUUIDFormatHash() ?? UUID() - let manualDevice = Device(id: hashedIdentifier, - name: "\(withConnectionString) (Manual)", - transportType: .tcp, identifier: withConnectionString) - try await AccessoryManager.shared.connect(to: manualDevice) + func device(forManualConnection connectionString: String) -> Device? { + let parts = connectionString.split(separator: ":") + var identifier: String + + switch parts.count { + case 1: + // host & default port + identifier = "\(parts[0]):4403" + + case 2: + // host & port + if parts[1].isValidTCPPort { + identifier = "\(parts[0]):\(parts[1])" + } else { + return nil + } + + default: + return nil + } + let hashedIdentifier = identifier.toUUIDFormatHash() + return Device(id: hashedIdentifier, + name: "\(connectionString) (Manual)", + transportType: .tcp, + identifier: connectionString, + isManualConnection: true) + } + + func manuallyConnect(toDevice device: Device) async throws { + try await AccessoryManager.shared.connect(to: device) } } +extension StringProtocol { + var isValidTCPPort: Bool { + // Check if the string is non-empty and contains only digits + guard !isEmpty, allSatisfy({ $0.isNumber }) else { + return false + } + + // Parse the string to an integer + guard let port = Int(self) else { + return false // Fails if the string can't be converted to an integer + } + + // Check if the port is in the valid TCP range (0–65535) + return port >= 0 && port <= 65535 + } +} + extension NetService { var ipv4Address: String? { for addressData in addresses ?? [] { diff --git a/Meshtastic/Assets.xcassets/HELTECV4.imageset/Contents.json b/Meshtastic/Assets.xcassets/HELTECV4.imageset/Contents.json new file mode 100644 index 00000000..a90a896f --- /dev/null +++ b/Meshtastic/Assets.xcassets/HELTECV4.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "heltec_v4.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Meshtastic/Assets.xcassets/HELTECV4.imageset/heltec_v4.svg b/Meshtastic/Assets.xcassets/HELTECV4.imageset/heltec_v4.svg new file mode 100644 index 00000000..849d056f --- /dev/null +++ b/Meshtastic/Assets.xcassets/HELTECV4.imageset/heltec_v4.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Meshtastic/Assets.xcassets/MUZIR1NEO.imageset/Contents.json b/Meshtastic/Assets.xcassets/MUZIR1NEO.imageset/Contents.json new file mode 100644 index 00000000..ced0ad0c --- /dev/null +++ b/Meshtastic/Assets.xcassets/MUZIR1NEO.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "muzi_r1_neo.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Meshtastic/Assets.xcassets/MUZIR1NEO.imageset/muzi_r1_neo.svg b/Meshtastic/Assets.xcassets/MUZIR1NEO.imageset/muzi_r1_neo.svg new file mode 100644 index 00000000..2f2cb0bf --- /dev/null +++ b/Meshtastic/Assets.xcassets/MUZIR1NEO.imageset/muzi_r1_neo.svg @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Meshtastic/Assets.xcassets/THINKNODEM3.imageset/Contents.json b/Meshtastic/Assets.xcassets/THINKNODEM3.imageset/Contents.json new file mode 100644 index 00000000..625b6450 --- /dev/null +++ b/Meshtastic/Assets.xcassets/THINKNODEM3.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "thinknode_m3.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Meshtastic/Assets.xcassets/THINKNODEM3.imageset/thinknode_m3.svg b/Meshtastic/Assets.xcassets/THINKNODEM3.imageset/thinknode_m3.svg new file mode 100644 index 00000000..ce5ce6fc --- /dev/null +++ b/Meshtastic/Assets.xcassets/THINKNODEM3.imageset/thinknode_m3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Meshtastic/Assets.xcassets/THINKNODEM4.imageset/Contents.json b/Meshtastic/Assets.xcassets/THINKNODEM4.imageset/Contents.json new file mode 100644 index 00000000..2c2709dc --- /dev/null +++ b/Meshtastic/Assets.xcassets/THINKNODEM4.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "thinknode_m4.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Meshtastic/Assets.xcassets/THINKNODEM4.imageset/thinknode_m4.svg b/Meshtastic/Assets.xcassets/THINKNODEM4.imageset/thinknode_m4.svg new file mode 100644 index 00000000..a5050859 --- /dev/null +++ b/Meshtastic/Assets.xcassets/THINKNODEM4.imageset/thinknode_m4.svg @@ -0,0 +1 @@ +T&HStatusFunction \ No newline at end of file diff --git a/Meshtastic/Enums/LoraConfigEnums.swift b/Meshtastic/Enums/LoraConfigEnums.swift index b196a7a7..7be12c7c 100644 --- a/Meshtastic/Enums/LoraConfigEnums.swift +++ b/Meshtastic/Enums/LoraConfigEnums.swift @@ -313,6 +313,7 @@ enum ModemPresets: Int, CaseIterable, Identifiable { case longFast = 0 case longSlow = 1 case longModerate = 7 + case longTurbo = 9 case medSlow = 3 case medFast = 4 case shortSlow = 5 @@ -328,6 +329,8 @@ enum ModemPresets: Int, CaseIterable, Identifiable { return "Long Range - Slow".localized case .longModerate: return "Long Range - Moderate".localized + case .longTurbo: + return "Long Range - Turbo".localized case .medSlow: return "Medium Range - Slow".localized case .medFast: @@ -348,6 +351,8 @@ enum ModemPresets: Int, CaseIterable, Identifiable { return "LongSlow" case .longModerate: return "LongModerate" + case .longTurbo: + return "LongTurbo" case .medSlow: return "MediumSlow" case .medFast: @@ -366,6 +371,8 @@ enum ModemPresets: Int, CaseIterable, Identifiable { return -17.5 case .longSlow: return -7.5 + case .longTurbo: + return -12.5 case .longModerate: return -17.5 case .medSlow: @@ -388,6 +395,8 @@ enum ModemPresets: Int, CaseIterable, Identifiable { return Config.LoRaConfig.ModemPreset.longSlow case .longModerate: return Config.LoRaConfig.ModemPreset.longModerate + case .longTurbo: + return Config.LoRaConfig.ModemPreset.longTurbo case .medSlow: return Config.LoRaConfig.ModemPreset.mediumSlow case .medFast: diff --git a/Meshtastic/Extensions/Bundle.swift b/Meshtastic/Extensions/Bundle.swift index 0bb62acf..9f710bb7 100644 --- a/Meshtastic/Extensions/Bundle.swift +++ b/Meshtastic/Extensions/Bundle.swift @@ -23,4 +23,12 @@ extension Bundle { public var isTestFlight: Bool { return appStoreReceiptURL?.lastPathComponent == "sandboxReceipt" } + + public var isDebug: Bool { + #if DEBUG + return true + #else + return false + #endif + } } diff --git a/Meshtastic/Extensions/CoreData/ChannelEntityExtension.swift b/Meshtastic/Extensions/CoreData/ChannelEntityExtension.swift index c85eef4a..8fce761c 100644 --- a/Meshtastic/Extensions/CoreData/ChannelEntityExtension.swift +++ b/Meshtastic/Extensions/CoreData/ChannelEntityExtension.swift @@ -9,22 +9,45 @@ import CoreData import MeshtasticProtobufs extension ChannelEntity { + var messagePredicate: NSPredicate { + return NSPredicate(format: "channel == %ld AND toUser == nil AND isEmoji == false", self.index) + } + + var messageFetchRequest: NSFetchRequest { + let fetchRequest = MessageEntity.fetchRequest() + fetchRequest.sortDescriptors = [NSSortDescriptor(key: "messageTimestamp", ascending: true)] + fetchRequest.predicate = messagePredicate + return fetchRequest + } var allPrivateMessages: [MessageEntity] { let context = PersistenceController.shared.container.viewContext - let fetchRequest = MessageEntity.fetchRequest() - fetchRequest.sortDescriptors = [NSSortDescriptor(key: "messageTimestamp", ascending: true)] - fetchRequest.predicate = NSPredicate(format: "channel == %ld AND toUser == nil AND isEmoji == false", self.index) + let fetchRequest = messageFetchRequest return (try? context.fetch(fetchRequest)) ?? [MessageEntity]() } - var unreadMessages: Int { + var mostRecentPrivateMessage: MessageEntity? { + // Most recent channel message (descending, limit 1) + let context = PersistenceController.shared.container.viewContext + let fetchRequest = messageFetchRequest + fetchRequest.sortDescriptors = [NSSortDescriptor(key: "messageTimestamp", ascending: false)] + fetchRequest.fetchLimit = 1 - let unreadMessages = allPrivateMessages.filter { ($0 as AnyObject).read == false } - return unreadMessages.count + return (try? context.fetch(fetchRequest))?.first } + func unreadMessages(context: NSManagedObjectContext) -> Int { + let fetchRequest = messageFetchRequest + fetchRequest.sortDescriptors = [] // sort is irrelevant. + fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [fetchRequest.predicate!, NSPredicate(format: "read == false")]) + + return (try? context.count(for: fetchRequest)) ?? 0 + } + + // Backwards-compatible property (uses viewContext) + var unreadMessages: Int { unreadMessages(context: PersistenceController.shared.container.viewContext) } + var protoBuf: Channel { var channel = Channel() channel.index = self.index @@ -32,7 +55,7 @@ extension ChannelEntity { channel.settings.psk = self.psk ?? Data() channel.role = Channel.Role(rawValue: Int(self.role)) ?? Channel.Role.secondary channel.settings.moduleSettings.positionPrecision = UInt32(self.positionPrecision) - channel.settings.moduleSettings.isClientMuted = self.mute + channel.settings.moduleSettings.isMuted = self.mute return channel } } diff --git a/Meshtastic/Extensions/CoreData/MessageEntityExtension.swift b/Meshtastic/Extensions/CoreData/MessageEntityExtension.swift index e7abb191..d6d2c997 100644 --- a/Meshtastic/Extensions/CoreData/MessageEntityExtension.swift +++ b/Meshtastic/Extensions/CoreData/MessageEntityExtension.swift @@ -5,10 +5,9 @@ // Created by Ben on 8/22/23. // -import Foundation - import CoreData import CoreLocation +import Foundation import MapKit import SwiftUI @@ -26,16 +25,62 @@ extension MessageEntity { var tapbacks: [MessageEntity] { let context = PersistenceController.shared.container.viewContext let fetchRequest = MessageEntity.fetchRequest() - fetchRequest.sortDescriptors = [NSSortDescriptor(key: "messageTimestamp", ascending: true)] - fetchRequest.predicate = NSPredicate(format: "replyID == %lld AND isEmoji == true", self.messageId) + fetchRequest.sortDescriptors = [ + NSSortDescriptor(key: "messageTimestamp", ascending: true) + ] + fetchRequest.predicate = NSPredicate( + format: "replyID == %lld AND isEmoji == true", + self.messageId + ) return (try? context.fetch(fetchRequest)) ?? [MessageEntity]() } func displayTimestamp(aboveMessage: MessageEntity?) -> Bool { if let aboveMessage = aboveMessage { - return aboveMessage.timestamp.addingTimeInterval(3600) < timestamp // 60 minutes + return aboveMessage.timestamp.addingTimeInterval(3600) < timestamp // 60 minutes + } + return false // First message will have no timestamp + } + + func relayDisplay() -> String? { + + guard self.relayNode != 0 else { return nil } + let context = PersistenceController.shared.container.viewContext + + let relaySuffix = Int64(self.relayNode & 0xFF) + let request: NSFetchRequest = UserEntity.fetchRequest() + request.predicate = NSPredicate( + format: "(num & 0xFF) == %lld", + relaySuffix + ) + + do { + let users = try context.fetch(request) + + // If exactly one match is found, return its name + if users.count == 1, let name = users.first?.longName, !name.isEmpty + { + return "\(name)" + } + + // If no exact match, find the node with the smallest hopsAway + if let closestNode = users.min(by: { lhs, rhs in + guard let lhsHops = lhs.userNode?.hopsAway, + let rhsHops = rhs.userNode?.hopsAway + else { + return false + } + return lhsHops < rhsHops + }), let name = closestNode.longName, !name.isEmpty { + return "\(name)" + } + + // Fallback to hex node number if no matches + return String(format: "Node 0x%02X", UInt32(self.relayNode & 0xFF)) + + } catch { + return String(format: "Node 0x%02X", UInt32(self.relayNode & 0xFF)) } - return false // First message will have no timestamp } } diff --git a/Meshtastic/Extensions/CoreData/MyInfoEntityExtension.swift b/Meshtastic/Extensions/CoreData/MyInfoEntityExtension.swift index 68a48ee1..c9e06d88 100644 --- a/Meshtastic/Extensions/CoreData/MyInfoEntityExtension.swift +++ b/Meshtastic/Extensions/CoreData/MyInfoEntityExtension.swift @@ -6,23 +6,39 @@ // import Foundation +import CoreData extension MyInfoEntity { + var messagePredicate: NSPredicate { + return NSPredicate(format: "toUser == nil AND isEmoji == false") + } + + var messageFetchRequest: NSFetchRequest { + let fetchRequest = MessageEntity.fetchRequest() + fetchRequest.sortDescriptors = [NSSortDescriptor(key: "messageTimestamp", ascending: true)] + fetchRequest.predicate = messagePredicate + return fetchRequest + } var messageList: [MessageEntity] { let context = PersistenceController.shared.container.viewContext - let fetchRequest = MessageEntity.fetchRequest() - fetchRequest.sortDescriptors = [NSSortDescriptor(key: "messageTimestamp", ascending: true)] - fetchRequest.predicate = NSPredicate(format: "toUser == nil") + let fetchRequest = messageFetchRequest - return (try? context.fetch(fetchRequest)) ?? [MessageEntity]() + return (try? context.fetch(messageFetchRequest)) ?? [MessageEntity]() } - var unreadMessages: Int { - let unreadMessages = messageList.filter { ($0 as AnyObject).read == false && ($0 as AnyObject).isEmoji == false } - return unreadMessages.count + func unreadMessages(context: NSManagedObjectContext) -> Int { + // Returns the count of unread *channel* messages + let fetchRequest = messageFetchRequest + fetchRequest.sortDescriptors = [] // sort is irrelevant. + fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [fetchRequest.predicate!, NSPredicate(format: "read == false")]) + + return (try? context.count(for: fetchRequest)) ?? 0 } + // Backwards-compatible property (uses viewContext) + var unreadMessages: Int { unreadMessages(context: PersistenceController.shared.container.viewContext) } + var hasAdmin: Bool { let adminChannel = channels?.filter { ($0 as AnyObject).name?.lowercased() == "admin" } return adminChannel?.count ?? 0 > 0 diff --git a/Meshtastic/Extensions/CoreData/PositionEntityExtension.swift b/Meshtastic/Extensions/CoreData/PositionEntityExtension.swift index 3efa9af3..080693c2 100644 --- a/Meshtastic/Extensions/CoreData/PositionEntityExtension.swift +++ b/Meshtastic/Extensions/CoreData/PositionEntityExtension.swift @@ -96,10 +96,40 @@ extension PositionEntity { } return pointAnn } + + var isPreciseLocation: Bool { + precisionBits == 32 || precisionBits == 0 + } + + var fuzzedNodeCoordinate: CLLocationCoordinate2D? { + // With reduced precisionBits, many nodes can overlap on the map, making them unclickable. + // Use a hash of the position ID to fuzz coordinate slightly so that these nodes can be distinguished at the higest zoom levels. This allows them to be clicked individually. + if latitudeI != 0 && longitudeI != 0 { + // Derive two uniform pseudorandom numbers [0,1) from id.hashValue + let u1 = Double(id.hashValue & 0xFFFF) / 65536.0 + let u2 = Double((id.hashValue >> 16) & 0xFFFF) / 65536.0 + + // Angle and radius + let offsetAngle = 2.0 * .pi * u1 + let offsetRadius = 0.00001 * sqrt(u2) // 1.0e-5 degrees at equator is about 1.11 m or 4 ft + + let dLat = sin(offsetAngle) * offsetRadius + let dLon = cos(offsetAngle) * offsetRadius + + let coord = CLLocationCoordinate2D( + latitude: latitude! + dLat, + longitude: longitude! + dLon + ) + return coord + } else { + return nil + } + } } extension PositionEntity: MKAnnotation { public var coordinate: CLLocationCoordinate2D { nodeCoordinate ?? LocationsHandler.DefaultLocation } + public var fuzzedCoordinate: CLLocationCoordinate2D { fuzzedNodeCoordinate ?? LocationsHandler.DefaultLocation } public var title: String? { nodePosition?.user?.shortName ?? "Unknown".localized } public var subtitle: String? { time?.formatted() } } diff --git a/Meshtastic/Extensions/CoreData/UserEntityExtension.swift b/Meshtastic/Extensions/CoreData/UserEntityExtension.swift index 2a87ed9f..3a61ff4b 100644 --- a/Meshtastic/Extensions/CoreData/UserEntityExtension.swift +++ b/Meshtastic/Extensions/CoreData/UserEntityExtension.swift @@ -10,16 +10,37 @@ import CoreData import MeshtasticProtobufs extension UserEntity { + var messagePredicate: NSPredicate { + return NSPredicate(format: "((toUser == %@) OR (fromUser == %@)) AND toUser != nil AND fromUser != nil AND isEmoji == false AND admin = false AND portNum != 10", self, self) + } + + var messageFetchRequest: NSFetchRequest { + let fetchRequest = MessageEntity.fetchRequest() + fetchRequest.sortDescriptors = [NSSortDescriptor(key: "messageTimestamp", ascending: true)] + fetchRequest.predicate = messagePredicate + return fetchRequest + } var messageList: [MessageEntity] { let context = PersistenceController.shared.container.viewContext - let fetchRequest = MessageEntity.fetchRequest() - fetchRequest.sortDescriptors = [NSSortDescriptor(key: "messageTimestamp", ascending: true)] - fetchRequest.predicate = NSPredicate(format: "((toUser == %@) OR (fromUser == %@)) AND toUser != nil AND fromUser != nil AND isEmoji == false AND admin = false AND portNum != 10", self, self) + let fetchRequest = messageFetchRequest return (try? context.fetch(fetchRequest)) ?? [MessageEntity]() } + var mostRecentMessage: MessageEntity? { + // Most contacts will have no DMs history, so we can return early. + guard self.lastMessage != nil else { return nil; } + + // Most recent DM for this user (descending, limit 1) + let context = PersistenceController.shared.container.viewContext + let fetchRequest = messageFetchRequest + fetchRequest.sortDescriptors = [NSSortDescriptor(key: "messageTimestamp", ascending: false)] + fetchRequest.fetchLimit = 1 + + return (try? context.fetch(fetchRequest))?.first + } + var sensorMessageList: [MessageEntity] { let context = PersistenceController.shared.container.viewContext let fetchRequest = MessageEntity.fetchRequest() @@ -29,10 +50,21 @@ extension UserEntity { return (try? context.fetch(fetchRequest)) ?? [MessageEntity]() } - var unreadMessages: Int { - let unreadMessages = messageList.filter { ($0 as AnyObject).read == false && ($0 as AnyObject).isEmoji == false } - return unreadMessages.count + func unreadMessages(context: NSManagedObjectContext, skipLastMessageCheck: Bool = false) -> Int { + // Most contacts will have no DMs history, so we can return early. + // (For our own node, set skipLastMessageCheck=true, because we don't update lastMessage on our own connected node.) + guard self.lastMessage != nil || skipLastMessageCheck else { return 0; } + + let fetchRequest = messageFetchRequest + fetchRequest.sortDescriptors = [] // sort is irrelevant. + fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [fetchRequest.predicate!, NSPredicate(format: "read == false")]) + + return (try? context.count(for: fetchRequest)) ?? 0 } + + // Backwards-compatible property (uses viewContext) + var unreadMessages: Int { unreadMessages(context: PersistenceController.shared.container.viewContext) } + /// SVG Images for Vendors who are signed project backers var hardwareImage: String? { guard let hwModel else { return nil } @@ -44,6 +76,8 @@ extension UserEntity { return "HELTECMESHNODET114" case "HELTECV3": return "HELTECV3" + case "HELTECV4": + return "HELTECV4" case "HELTECMESHPOCKET": return "HELTECMESHPOCKET" case "HELTECVISIONMASTERE213": @@ -102,8 +136,20 @@ extension UserEntity { return "NANOG1" case "NANOG2ULTRA": return "NANOG2ULTRA" + /// Muzi Works + case "MUZIR1NEO": + return "MUZIR1NEO" case "STATIONG2": return "STATIONG2" + /// Elecrow + case "THINKNODEM1": + return "THINKNODEM1" + case "THINKNODEM2": + return "THINKNODEM2" + case "THINKNODEM3": + return "THINKNODEM3" + case "THINKNODEM4": + return "THINKNODEM4" /// DIY Devices case "RPIPICO": return "RPIPICO" @@ -130,6 +176,7 @@ public func createUser(num: Int64, context: NSManagedObjectContext) throws -> Us newUser.longName = "Meshtastic \(last4)" newUser.shortName = last4 newUser.hwModel = "UNSET" + newUser.unmessagable = false } return newUser diff --git a/Meshtastic/Extensions/Url.swift b/Meshtastic/Extensions/Url.swift index 150bab42..fab43b98 100644 --- a/Meshtastic/Extensions/Url.swift +++ b/Meshtastic/Extensions/Url.swift @@ -31,6 +31,18 @@ extension URL { return nil } } + var queryParameters: [String: String]? { + guard let components = URLComponents(url: self, resolvingAgainstBaseURL: true), + let queryItems = components.queryItems else { + return nil + } + + var parameters = [String: String]() + for item in queryItems { + parameters[item.name] = item.value + } + return parameters + } var attributes: [FileAttributeKey: Any]? { do { return try FileManager.default.attributesOfItem(atPath: path) diff --git a/Meshtastic/Extensions/UserDefaults.swift b/Meshtastic/Extensions/UserDefaults.swift index d5a58e13..82e67773 100644 --- a/Meshtastic/Extensions/UserDefaults.swift +++ b/Meshtastic/Extensions/UserDefaults.swift @@ -6,6 +6,7 @@ // import Foundation +import OSLog @propertyWrapper struct UserDefault { @@ -79,6 +80,7 @@ extension UserDefaults { case showDeviceOnboarding case usageDataAndCrashReporting case autoconnectOnDiscovery + case manualConnections case testIntEnum } @@ -178,6 +180,36 @@ extension UserDefaults { @UserDefault(.testIntEnum, defaultValue: .one) static var testIntEnum: TestIntEnum + + static var manualConnections: [Device] { + get { + // Retrieve data from UserDefaults + guard let data = UserDefaults.standard.data(forKey: Keys.manualConnections.rawValue) else { + return [] + } + + // Decode the Data back to [Device] + do { + let decoder = JSONDecoder() + let devices = try decoder.decode([Device].self, from: data) + return devices + } catch { + return [] + } + } + set { + do { + // Encode the [Device] to Data + let encoder = JSONEncoder() + let data = try encoder.encode(newValue) + + // Store the Data in UserDefaults + UserDefaults.standard.set(data, forKey: Keys.manualConnections.rawValue) + } catch { + Logger.transport.error("💥 Failed to encode manualConnections: \(error, privacy: .public)") + } + } + } } enum TestIntEnum: Int, Decodable { diff --git a/Meshtastic/Helpers/LocationsHandler.swift b/Meshtastic/Helpers/LocationsHandler.swift index b174970a..6d44499d 100644 --- a/Meshtastic/Helpers/LocationsHandler.swift +++ b/Meshtastic/Helpers/LocationsHandler.swift @@ -26,6 +26,8 @@ import OSLog @Published var recordingStarted: Date? @Published var distanceTraveled = 0.0 @Published var elevationGain = 0.0 + @Published var heading: Double = 0.0 // Current heading in degrees + @Published var headingUpdatesStarted: Bool = false // Track heading updates state @Published var updatesStarted: Bool = UserDefaults.standard.bool(forKey: "liveUpdatesStarted") { @@ -131,6 +133,10 @@ import OSLog self.manager.desiredAccuracy = kCLLocationAccuracyBest // Set the distance filter to only receive updates when the device has moved a certain distance. self.manager.distanceFilter = kCLDistanceFilterNone // Receive all updates initially + if CLLocationManager.headingAvailable() { + self.manager.headingFilter = 1 // Update heading when it changes by 1 degree + self.manager.headingOrientation = .portrait // Adjust based on device orientation + } } func startLocationUpdates() { @@ -178,6 +184,39 @@ import OSLog // The Task completes implicitly here. } } + + // New method to start heading updates + func startHeadingUpdates() { + guard CLLocationManager.headingAvailable() else { + Logger.services.warning("📍 [App] Heading updates not available on this device.") + return + } + + guard manager.authorizationStatus == .authorizedAlways || manager.authorizationStatus == .authorizedWhenInUse else { + Logger.services.warning("📍 [App] Cannot start heading updates: insufficient authorization status.") + return + } + + Logger.services.info("📍 [App] Starting heading updates") + manager.startUpdatingHeading() + headingUpdatesStarted = true + } + + // New method to stop heading updates + func stopHeadingUpdates() { + Logger.services.info("🛑 [App] Stopping heading updates") + manager.stopUpdatingHeading() + headingUpdatesStarted = false + } + + // Implement the CLLocationManagerDelegate method for heading updates + func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) { + // Update heading on the main thread + Task { @MainActor in + self.heading = newHeading.trueHeading >= 0 ? newHeading.trueHeading : newHeading.magneticHeading + } + } + /// Stops receiving live location updates. func stopLocationUpdates() { Logger.services.info("🛑 [App] Stopping location updates") diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 4d217dc7..255417c4 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -181,7 +181,7 @@ func channelPacket (channel: Channel, fromNum: Int64, context: NSManagedObjectCo newChannel.psk = channel.settings.psk if channel.settings.hasModuleSettings { newChannel.positionPrecision = Int32(truncatingIfNeeded: channel.settings.moduleSettings.positionPrecision) - newChannel.mute = channel.settings.moduleSettings.isClientMuted + newChannel.mute = channel.settings.moduleSettings.isMuted } guard let mutableChannels = fetchedMyInfo[0].channels!.mutableCopy() as? NSMutableOrderedSet else { return @@ -264,7 +264,7 @@ func deviceMetadataPacket (metadata: DeviceMetadata, fromNum: Int64, sessionPass } } -func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObjectContext) -> NodeInfoEntity? { +func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObjectContext, deferSave: Bool = false) -> NodeInfoEntity? { let logString = String.localizedStringWithFormat("[NodeInfo] received for: %@".localized, String(nodeInfo.num)) Logger.mesh.info("📟 \(logString, privacy: .public)") @@ -375,8 +375,10 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje newNode.myInfo = fetchedMyInfo[0] } do { - try context.save() - Logger.data.info("💾 Saved a new Node Info For: \(String(nodeInfo.num), privacy: .public)") + if !deferSave { + try context.save() + Logger.data.info("💾 Saved a new Node Info For: \(String(nodeInfo.num), privacy: .public)") + } return newNode } catch { context.rollback() @@ -500,8 +502,10 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje fetchedNode[0].myInfo = fetchedMyInfo[0] } do { - try context.save() - Logger.data.info("💾 [NodeInfo] saved for \(nodeInfo.num.toHex(), privacy: .public)") + if !deferSave { + try context.save() + Logger.data.info("💾 [NodeInfo] saved for \(nodeInfo.num.toHex(), privacy: .public)") + } return fetchedNode[0] } catch { context.rollback() @@ -620,6 +624,7 @@ func adminResponseAck (packet: MeshPacket, context: NSManagedObjectContext) { fetchedMessage[0].ackError = Int32(RoutingError.none.rawValue) fetchedMessage[0].receivedACK = true fetchedMessage[0].realACK = true + fetchedMessage[0].relayNode = Int64(packet.relayNode) fetchedMessage[0].ackSNR = packet.rxSnr if fetchedMessage[0].fromUser != nil { fetchedMessage[0].fromUser?.objectWillChange.send() @@ -695,9 +700,11 @@ func routingPacket (packet: MeshPacket, connectedNodeNum: Int64, context: NSMana fetchedMessage[0].realACK = true } } + fetchedMessage[0].relayNode = Int64(packet.relayNode) fetchedMessage[0].ackError = Int32(routingMessage.errorReason.rawValue) if routingMessage.errorReason == Routing.Error.none { fetchedMessage[0].receivedACK = true + fetchedMessage[0].relays += 1 } fetchedMessage[0].ackSNR = packet.rxSnr @@ -940,6 +947,9 @@ func textMessageAppPacket( } else { newMessage.messageTimestamp = Int32(Date().timeIntervalSince1970) } + if packet.relayNode != 0 { + newMessage.relayNode = Int64(packet.relayNode) + } newMessage.receivedACK = false newMessage.snr = packet.rxSnr newMessage.rssi = packet.rxRssi @@ -979,6 +989,7 @@ func textMessageAppPacket( newMessage.pkiEncrypted = true newMessage.publicKey = packet.publicKey } + /// Check for key mismatch if let nodeKey = newMessage.fromUser?.publicKey { if newMessage.toUser != nil && packet.pkiEncrypted && !packet.publicKey.isEmpty { @@ -1040,7 +1051,10 @@ func textMessageAppPacket( if newMessage.fromUser != nil && newMessage.toUser != nil { // Set Unread Message Indicators if packet.to == connectedNode { - appState?.unreadDirectMessages = newMessage.toUser?.unreadMessages ?? 0 + let unreadCount = newMessage.toUser?.unreadMessages(context: context, skipLastMessageCheck: true) ?? 0 // skipLastMessageCheck=true because we don't update lastMessage on our own connected node + Task { @MainActor in + appState?.unreadDirectMessages = unreadCount + } } if !(newMessage.fromUser?.mute ?? false) && newMessage.isEmoji == false { // Create an iOS Notification for the received DM message @@ -1068,7 +1082,7 @@ func textMessageAppPacket( do { let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) if !fetchedMyInfo.isEmpty { - appState?.unreadChannelMessages = fetchedMyInfo[0].unreadMessages + appState?.unreadChannelMessages = fetchedMyInfo[0].unreadMessages(context: context) for channel in (fetchedMyInfo[0].channels?.array ?? []) as? [ChannelEntity] ?? [] { if channel.index == newMessage.channel { context.refresh(channel, mergeChanges: true) diff --git a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift index 853533e9..936f23ad 100644 --- a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift +++ b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift @@ -25,6 +25,7 @@ class MqttClientProxyManager { var mqttClientProxy: CocoaMQTT? var topic = "msh" var debugLog = false + var shouldSubscribe = true func connectFromConfigSettings(node: NodeInfoEntity) { let originalAddress = node.mqttConfig?.address ?? "mqtt.meshtastic.org" let defaultServerAddress = "mqtt.meshtastic.org" @@ -43,6 +44,20 @@ class MqttClientProxyManager { let port = defaultServerPort let root = node.mqttConfig?.root?.count ?? 0 > 0 ? node.mqttConfig?.root : "msh" let prefix = root! + // Safely iterate channels and determine if any has downlink enabled + var hasAnyDownlinkEnabled = false + if let anyChannels = node.myInfo?.channels as? NSOrderedSet { + let channelEntities: [ChannelEntity] = anyChannels.array.compactMap { $0 as? ChannelEntity } + for channel in channelEntities { + if channel.downlinkEnabled == true { + hasAnyDownlinkEnabled = true + break + } + } + } + + shouldSubscribe = hasAnyDownlinkEnabled + topic = prefix + "/2/e" + "/#" // Require opt in to map report terms to connect if node.mqttConfig?.mapReportingEnabled ?? false && UserDefaults.mapReportingOptIn || !(node.mqttConfig?.mapReportingEnabled ?? false) { @@ -169,3 +184,4 @@ extension MqttClientProxyManager: CocoaMQTTDelegate { Logger.mqtt.debug("📲 [MQTT Client Proxy] pong") } } + diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 55.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 55.xcdatamodel/contents index b5e4a81e..a6e5465f 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 55.xcdatamodel/contents +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 55.xcdatamodel/contents @@ -1,5 +1,5 @@ - + @@ -164,6 +164,8 @@ + + diff --git a/Meshtastic/MeshtasticApp.swift b/Meshtastic/MeshtasticApp.swift index a83ddb9b..d60ed940 100644 --- a/Meshtastic/MeshtasticApp.swift +++ b/Meshtastic/MeshtasticApp.swift @@ -11,6 +11,7 @@ import DatadogRUM import DatadogTrace import DatadogLogs import DatadogSessionReplay + @main struct MeshtasticAppleApp: App { @@ -19,10 +20,8 @@ struct MeshtasticAppleApp: App { private let persistenceController: PersistenceController private let accessoryManager: AccessoryManager @Environment(\.scenePhase) var scenePhase - @State var saveChannels = false + @State var saveChannelLink: SaveChannelLinkData? @State var incomingUrl: URL? - @State var channelSettings: String? - @State var addChannels = false init() { @@ -36,7 +35,7 @@ struct MeshtasticAppleApp: App { let appID = "79fe92a9-74c9-4c8f-ba63-6308384ecfa9" let clientToken = "pub4427bea20dbdb08a6af68034de22cd3b" var environment = "AppStore" - + #if DEBUG environment = "Local" #else @@ -44,7 +43,7 @@ struct MeshtasticAppleApp: App { environment = "TestFlight" } #endif - + Datadog.initialize( with: Datadog.Configuration( clientToken: clientToken, @@ -57,7 +56,7 @@ struct MeshtasticAppleApp: App { Logs.enable() Trace.enable( with: Trace.Configuration( - sampleRate: 100, networkInfoEnabled: true // 100% sampling for development/testing, reduce for production + sampleRate: 100, networkInfoEnabled: true // 100% sampling for development/testing, reduce for production ) ) @@ -98,32 +97,54 @@ struct MeshtasticAppleApp: App { #endif if !UserDefaults.firstLaunch { // If this is first launch, we will show onboarding screens which - // Step through the authorization process. Do not start discovery + // Step through the authorization process. Do not start discovery // unitl this workflow completes, otherwise the discovery process // may trigger permission dialogs too soon. accessoryManager.startDiscovery() } } - var body: some Scene { - WindowGroup { + + private func handleChannelLinkURL(_ url: URL, fromActivity: Bool) { + // Reset the state before processing a new URL + self.saveChannelLink = nil + + guard url.absoluteString.lowercased().contains("meshtastic.org/e/") else { + return + } + + let queryParams = url.queryParameters + let addChannels = Bool(queryParams?["add"] ?? "false") ?? false + var channelData: String? + let urlString = url.absoluteString + + if let fragment = urlString.components(separatedBy: "#").last, !fragment.isEmpty { + channelData = fragment.components(separatedBy: "?").first + } + + guard let finalChannelData = channelData, !finalChannelData.isEmpty else { + Logger.mesh.error("Could not extract channel data from URL: \(url.absoluteString, privacy: .public)") + return + } + + self.saveChannelLink = SaveChannelLinkData(data: finalChannelData, add: addChannels) + Logger.services.debug("Add Channel \(addChannels, privacy: .public) with data: \(finalChannelData, privacy: .public)") + + // Log based on the calling context + let source = fromActivity ? "User Activity" : "Open URL" + Logger.mesh.debug("User wants to open a Channel Settings URL (\(source)): \(url.absoluteString, privacy: .public)") + } + + var body: some Scene { + WindowGroup { ContentView( appState: appState, router: appState.router ) - .sheet(isPresented: Binding( - get: { - saveChannels && !(channelSettings == nil) - }, - set: { newValue in - saveChannels = newValue - if !newValue { - channelSettings = nil - } - } - )) { + .sheet(item: $saveChannelLink + ) { link in SaveChannelQRCode( - channelSetLink: channelSettings ?? "Empty Channel URL", - addChannels: addChannels, + channelSetLink: link.data, + addChannels: link.add, // <-- Uses the now reliable 'add' boolean accessoryManager: accessoryManager ) .presentationDetents([.large]) .presentationDragIndicator(.visible) @@ -131,55 +152,30 @@ struct MeshtasticAppleApp: App { .onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { userActivity in Logger.mesh.debug("URL received \(userActivity, privacy: .public)") self.incomingUrl = userActivity.webpageURL - self.saveChannels = false - if self.incomingUrl?.absoluteString.lowercased().contains("meshtastic.org/v/#") == true { - ContactURLHandler.handleContactUrl(url: self.incomingUrl!, accessoryManager: accessoryManager) - } else if self.incomingUrl?.absoluteString.lowercased().contains("meshtastic.org/e/") == true { - if let components = self.incomingUrl?.absoluteString.components(separatedBy: "#") { - self.addChannels = Bool(self.incomingUrl?["add"] ?? "false") ?? false - if (self.incomingUrl?.absoluteString.lowercased().contains("?")) != nil { - guard let cs = components.last!.components(separatedBy: "?").first else { - return - } - self.channelSettings = cs - } else { - guard let cs = components.first else { - return - } - self.channelSettings = cs - } - Logger.services.debug("Add Channel \(self.addChannels, privacy: .public)") + self.saveChannelLink = nil + + if let url = userActivity.webpageURL { + if url.absoluteString.lowercased().contains("meshtastic.org/v/#") == true { + ContactURLHandler.handleContactUrl(url: url, accessoryManager: accessoryManager) + } else if url.absoluteString.lowercased().contains("meshtastic.org/e/") == true { + // **Consolidated Call for User Activity** + handleChannelLinkURL(url, fromActivity: true) } - self.saveChannels = true - Logger.mesh.debug("User wants to open a Channel Settings URL: \(self.incomingUrl?.absoluteString ?? "No QR Code Link")") } - if self.saveChannels { + + if self.saveChannelLink != nil { Logger.mesh.debug("User wants to open Channel Settings URL: \(String(describing: self.incomingUrl!.relativeString), privacy: .public)") } } .onOpenURL(perform: { (url) in Logger.mesh.debug("Some sort of URL was received \(url, privacy: .public)") self.incomingUrl = url + if url.absoluteString.lowercased().contains("meshtastic.org/v/#") { ContactURLHandler.handleContactUrl(url: url, accessoryManager: accessoryManager) } else if url.absoluteString.lowercased().contains("meshtastic.org/e/") { - if let components = self.incomingUrl?.absoluteString.components(separatedBy: "#") { - self.addChannels = Bool(self.incomingUrl?["add"] ?? "false") ?? false - if self.incomingUrl?.absoluteString.lowercased().contains("?") != nil { - guard let cs = components.last!.components(separatedBy: "?").first else { - return - } - self.channelSettings = cs - } else { - guard let cs = components.first else { - return - } - self.channelSettings = cs - } - Logger.services.debug("Add Channel \(self.addChannels, privacy: .public)") - } - self.saveChannels = true - Logger.mesh.debug("User wants to open a Channel Settings URL: \(self.incomingUrl?.absoluteString ?? "No QR Code Link", privacy: .public)") + // **Consolidated Call for Open URL** + handleChannelLinkURL(url, fromActivity: false) } else if url.absoluteString.lowercased().contains("meshtastic:///") { appState.router.route(url: url) } @@ -194,7 +190,7 @@ struct MeshtasticAppleApp: App { .displayFrequency(.immediate) ] ) - } + } } .onChange(of: scenePhase) { (_, newScenePhase) in switch newScenePhase { @@ -221,7 +217,5 @@ struct MeshtasticAppleApp: App { .environment(\.managedObjectContext, persistenceController.container.viewContext) .environmentObject(appState) .environmentObject(accessoryManager) - .environmentObject(appState.router) } - } diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index 3f758329..c56644a5 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -165,6 +165,60 @@ public func clearCoreDataDatabase(context: NSManagedObjectContext, includeRoutes } } +func updateAnyPacketFrom (packet: MeshPacket, activeDeviceNum: Int64, context: NSManagedObjectContext) { + // Update NodeInfoEntity for any packet received. This mirrors the firmware's NodeDB::updateFrom, which sniffs ALL received packets and updates the radio's nodeDB with packet.from's: + // - last_heard (from rxTime) + // - snr + // - via_mqtt + // - hops_away + + // However, unlike the firmware, this function will NOT create a new NodeInfoEntity if we don't have it already. We'll leave that to the existing code paths. + + // We do NOT update fetchedNode[0].channel, because we may hear a node over multiple channels, and only some packet types should update what we consider the node's channel to be. (Example: primary private channel, secondary public channel. A text message on the secondary public channel should NOT change fetchedNode[0].channel.) + + guard packet.from > 0 else { return } + guard packet.from != activeDeviceNum else { return } // Ignore if packet is from our own node + + let fetchNodeInfoAppRequest = NodeInfoEntity.fetchRequest() + fetchNodeInfoAppRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from)) + + do { + let fetchedNode = try context.fetch(fetchNodeInfoAppRequest) + if fetchedNode.count >= 1 { + fetchedNode[0].id = Int64(packet.from) + fetchedNode[0].num = Int64(packet.from) + + if packet.rxTime > 0 { + fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(packet.rxTime))) + Logger.data.info("💾 [updateAnyPacketFrom] Updating node \(packet.from.toHex(), privacy: .public) lastHeard from rxTime=\(packet.rxTime)") + } else { + fetchedNode[0].lastHeard = Date() + Logger.data.info("💾 [updateAnyPacketFrom] Updating node \(packet.from.toHex(), privacy: .public) lastHeard to now (rxTime==0)") + } + + fetchedNode[0].snr = packet.rxSnr + fetchedNode[0].rssi = packet.rxRssi + fetchedNode[0].viaMqtt = packet.viaMqtt + + if packet.hopStart != 0 && packet.hopLimit <= packet.hopStart { + fetchedNode[0].hopsAway = Int32(packet.hopStart - packet.hopLimit) + Logger.data.info("💾 [updateAnyPacketFrom] Updating node \(packet.from.toHex(), privacy: .public) hopsAway=\(fetchedNode[0].hopsAway)") + } + + do { + try context.save() + Logger.data.info("💾 [updateAnyPacketFrom] Updating node \(fetchedNode[0].num.toHex(), privacy: .public) snr=\(fetchedNode[0].snr), rssi=\(fetchedNode[0].rssi) from packet \(packet.id.toHex(), privacy: .public)") + } catch { + context.rollback() + let nsError = error as NSError + Logger.data.error("💥 [updateAnyPacketFrom] Error Saving node \(fetchedNode[0].num.toHex(), privacy: .public) from packet \(packet.id.toHex(), privacy: .public) \(nsError, privacy: .public)") + } + } + } catch { + Logger.data.error("💥 [updateAnyPacketFrom] fetch data error") + } +} + func upsertNodeInfoPacket (packet: MeshPacket, favorite: Bool = false, context: NSManagedObjectContext) { let logString = String.localizedStringWithFormat("[NodeInfo] received for: %@".localized, packet.from.toHex()) @@ -316,16 +370,6 @@ func upsertNodeInfoPacket (packet: MeshPacket, favorite: Bool = false, context: } else { // Update an existing node - fetchedNode[0].id = Int64(packet.from) - fetchedNode[0].num = Int64(packet.from) - if packet.rxTime > 0 { - fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(packet.rxTime))) - } else { - fetchedNode[0].lastHeard = Date() - } - fetchedNode[0].snr = packet.rxSnr - fetchedNode[0].rssi = packet.rxRssi - fetchedNode[0].viaMqtt = packet.viaMqtt if packet.to == Constants.maximumNodeNum || packet.to == UserDefaults.preferredPeripheralNum { fetchedNode[0].channel = Int32(packet.channel) } @@ -463,18 +507,7 @@ func upsertPositionPacket (packet: MeshPacket, context: NSManagedObjectContext) mutablePositions.removeAllObjects() } mutablePositions.add(position) - fetchedNode[0].id = Int64(packet.from) - fetchedNode[0].num = Int64(packet.from) - if positionMessage.time > 0 { - fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(positionMessage.time))) - } else if packet.rxTime > 0 { - fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(packet.rxTime))) - } else { - fetchedNode[0].lastHeard = Date() - } - fetchedNode[0].snr = packet.rxSnr - fetchedNode[0].rssi = packet.rxRssi - fetchedNode[0].viaMqtt = packet.viaMqtt + fetchedNode[0].channel = Int32(packet.channel) fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet @@ -1499,6 +1532,7 @@ func upsertTelemetryModuleConfigPacket(config: ModuleConfig.TelemetryConfig, nod if fetchedNode[0].telemetryConfig == nil { let newTelemetryConfig = TelemetryConfigEntity(context: context) newTelemetryConfig.deviceUpdateInterval = Int32(truncatingIfNeeded: config.deviceUpdateInterval) + newTelemetryConfig.deviceTelemetryEnabled = config.deviceTelemetryEnabled newTelemetryConfig.environmentUpdateInterval = Int32(truncatingIfNeeded: config.environmentUpdateInterval) newTelemetryConfig.environmentMeasurementEnabled = config.environmentMeasurementEnabled newTelemetryConfig.environmentScreenEnabled = config.environmentScreenEnabled @@ -1509,6 +1543,7 @@ func upsertTelemetryModuleConfigPacket(config: ModuleConfig.TelemetryConfig, nod fetchedNode[0].telemetryConfig = newTelemetryConfig } else { fetchedNode[0].telemetryConfig?.deviceUpdateInterval = Int32(truncatingIfNeeded: config.deviceUpdateInterval) + fetchedNode[0].telemetryConfig?.deviceTelemetryEnabled = config.deviceTelemetryEnabled fetchedNode[0].telemetryConfig?.environmentUpdateInterval = Int32(truncatingIfNeeded: config.environmentUpdateInterval) fetchedNode[0].telemetryConfig?.environmentMeasurementEnabled = config.environmentMeasurementEnabled fetchedNode[0].telemetryConfig?.environmentScreenEnabled = config.environmentScreenEnabled diff --git a/Meshtastic/Resources/DeviceHardware.json b/Meshtastic/Resources/DeviceHardware.json index 1306cfd2..3db1437d 100644 --- a/Meshtastic/Resources/DeviceHardware.json +++ b/Meshtastic/Resources/DeviceHardware.json @@ -908,6 +908,22 @@ "thinknode_m2.svg" ] }, + { + "hwModel": 93, + "hwModelSlug": "MUZI_BASE", + "platformioTarget": "muzi-base", + "architecture": "nrf52840", + "activelySupported": false, + "supportLevel": 1, + "displayName": "muzi base", + "tags": [ + "muzi" + ], + "requiresDfu": true, + "images": [ + "muzi_base.svg" + ] + }, { "hwModel": 94, "hwModelSlug": "HELTEC_MESH_POCKET", @@ -923,7 +939,28 @@ "heltec_mesh_pocket.svg" ], "requiresDfu": true, - "hasInkHud": true + "hasInkHud": true, + "key": "HELTEC_MESH_POCKET", + "variant": "10000mAh" + }, + { + "hwModel": 94, + "hwModelSlug": "HELTEC_MESH_POCKET", + "platformioTarget": "heltec-mesh-pocket-5000", + "architecture": "nrf52840", + "activelySupported": true, + "supportLevel": 1, + "displayName": "Heltec MeshPocket", + "tags": [ + "Heltec" + ], + "images": [ + "heltec_mesh_pocket.svg" + ], + "requiresDfu": true, + "hasInkHud": true, + "key": "HELTEC_MESH_POCKET", + "variant": "5000mAh" }, { "hwModel": 95, @@ -1051,7 +1088,7 @@ "hwModelSlug": "MUZI_R1_NEO", "platformioTarget": "r1-neo", "architecture": "nrf52840", - "activelySupported": false, + "activelySupported": true, "supportLevel": 1, "displayName": "muzi R1 Neo", "tags": [ @@ -1181,5 +1218,70 @@ "images": [ "rak_3312.svg" ] + }, + { + "hwModel": 115, + "hwModelSlug": "THINKNODE_M3", + "platformioTarget": "thinknode_m3", + "architecture": "nrf52840", + "activelySupported": true, + "supportLevel": 1, + "displayName": "ThinkNode M3", + "tags": [ + "Elecrow" + ], + "requiresDfu": true, + "images": [ + "thinknode_m3.svg" + ] + }, + { + "hwModel": 116, + "hwModelSlug": "WISMESH_TAP_V2", + "platformioTarget": "rak_wismesh_tap_v2", + "architecture": "esp32-s3", + "activelySupported": false, + "supportLevel": 1, + "displayName": "RAK WisMesh Tap V2", + "tags": [ + "RAK" + ], + "hasMui": true, + "partitionScheme": "8MB", + "images": [ + "rak-wismesh-tap-v2.svg" + ] + }, + { + "hwModel": 119, + "hwModelSlug": "THINKNODE_M4", + "platformioTarget": "thinknode_m4", + "architecture": "nrf52840", + "activelySupported": false, + "supportLevel": 1, + "displayName": "ThinkNode M4", + "tags": [ + "Elecrow" + ], + "requiresDfu": true, + "images": [ + "thinknode_m4.svg" + ] + }, + { + "hwModel": 120, + "hwModelSlug": "THINKNODE_M6", + "platformioTarget": "thinknode_m6", + "architecture": "nrf52840", + "activelySupported": false, + "supportLevel": 1, + "displayName": "ThinkNode M6", + "tags": [ + "Elecrow" + ], + "requiresDfu": true, + "images": [ + "thinknode_m6.svg" + ] } ] diff --git a/Meshtastic/Views/Connect/Connect.swift b/Meshtastic/Views/Connect/Connect.swift index 8915d9ff..bb43ae04 100644 --- a/Meshtastic/Views/Connect/Connect.swift +++ b/Meshtastic/Views/Connect/Connect.swift @@ -26,8 +26,7 @@ struct Connect: View { @State var isUnsetRegion = false @State var invalidFirmwareVersion = false @State var liveActivityStarted = false - @State var presentingSwitchPreferredPeripheral = false - @State var selectedPeripherialId = "" + @ObservedObject var manualConnections = ManualConnectionList.shared var body: some View { NavigationStack { @@ -264,61 +263,33 @@ struct Connect: View { .textCase(nil) if !(accessoryManager.isConnected || accessoryManager .isConnecting) { - Section(header: HStack { - Text("Available Radios").font(.title) - Spacer() - ManualConnectionMenu() - }) { - ForEach(accessoryManager.devices.sorted(by: { $0.name < $1.name })) { device in - HStack { - if UserDefaults.preferredPeripheralId == device.id.uuidString { - Image(systemName: "star.fill") - .imageScale(.large).foregroundColor(.yellow) - .padding(.trailing) - } else { - Image(systemName: "circle.fill") - .imageScale(.large).foregroundColor(.gray) - .padding(.trailing) - } - VStack(alignment: .leading) { - Button(action: { - if UserDefaults.preferredPeripheralId.count > 0 && device.id.uuidString != UserDefaults.preferredPeripheralId { - if accessoryManager.allowDisconnect { - Task { try await accessoryManager.disconnect() } - } - presentingSwitchPreferredPeripheral = true - selectedPeripherialId = device.id.uuidString - } else { - Task { - try? await accessoryManager.connect(to: device) + Group { + Section(header: HStack { + Text("Available Radios").font(.title) + Spacer() + ManualConnectionMenu() + }) { + ForEach(accessoryManager.devices.sorted(by: { $0.name < $1.name })) { device in + DeviceConnectRow(device: device) + } + } + if manualConnections.connectionsList.count > 0 { + Section(header: Text("Manual Connections").font(.title)) { + ForEach(manualConnections.connectionsList) { device in + DeviceConnectRow(device: device) +#if targetEnvironment(macCatalyst) + .contextMenu { + Button { + manualConnections.remove(device: device) + } label: { + Label("Delete", systemImage: "trash") } } - }) { - Text(device.name).font(.callout) - } - // Show transport type - TransportIcon(transportType: device.transportType) - } - Spacer() - VStack { - device.getSignalStrength().map { SignalStrengthIndicator(signalStrength: $0) } - } - }.padding([.bottom, .top]) - } - } - .confirmationDialog("Connecting to a new radio will clear all app data on the phone.", isPresented: $presentingSwitchPreferredPeripheral, titleVisibility: .visible) { - Button("Connect to new radio?", role: .destructive) { - UserDefaults.preferredPeripheralId = selectedPeripherialId - UserDefaults.preferredPeripheralNum = 0 - if accessoryManager.allowDisconnect { - Task { try await accessoryManager.disconnect() } - } - clearCoreDataDatabase(context: context, includeRoutes: false) - clearNotifications() - if let radio = accessoryManager.devices.first(where: { $0.id.uuidString == selectedPeripherialId }) { - Task { - try await accessoryManager.connect(to: radio) +#endif + }.onDelete { offsets in + manualConnections.remove(atOffsets: offsets) } + } } } @@ -472,6 +443,10 @@ struct TransportIcon: View { } struct ManualConnectionMenu: View { + + @EnvironmentObject var accessoryManager: AccessoryManager + @Environment(\.managedObjectContext) var context + private struct IterableTransport: Identifiable { let id: UUID let icon: Image @@ -490,7 +465,9 @@ struct ManualConnectionMenu: View { @State private var selectedTransport: IterableTransport? @State private var showAlert: Bool = false @State private var connectionString = "" - + @State var presentingSwitchPreferredPeripheral = false + @State var deviceForManualConnection: Device? + var body: some View { Menu { ForEach(transports) { transport in @@ -520,11 +497,114 @@ struct ManualConnectionMenu: View { Button("OK", action: { if !connectionString.isEmpty { - Task { - try await selectedTransport.transport.manuallyConnect(withConnectionString: connectionString) + if let device = selectedTransport.transport.device(forManualConnection: connectionString) { + if UserDefaults.preferredPeripheralId == device.id.uuidString { + Task { + try await selectedTransport.transport.manuallyConnect(toDevice: device) + } + } else { + deviceForManualConnection = device + presentingSwitchPreferredPeripheral = true + } } } }) - } + }.confirmationDialog("Connecting to a new radio will clear all app data on the phone.", isPresented: $presentingSwitchPreferredPeripheral, titleVisibility: .visible) { + Button("Connect to new radio?", role: .destructive) { + if let device = deviceForManualConnection { + UserDefaults.preferredPeripheralId = device.id.uuidString + UserDefaults.preferredPeripheralNum = 0 + if accessoryManager.allowDisconnect { + Task { try await accessoryManager.disconnect() } + } + clearCoreDataDatabase(context: context, includeRoutes: false) + clearNotifications() + Task { + try await selectedTransport?.transport.manuallyConnect(toDevice: device) + } + + // Clean up just in case + deviceForManualConnection = nil + } + } + } } } + +struct DeviceConnectRow: View { + @Environment(\.managedObjectContext) var context + @EnvironmentObject var accessoryManager: AccessoryManager + @State var presentingSwitchPreferredPeripheral = false + let device: Device + + var body: some View { + HStack { + if UserDefaults.preferredPeripheralId == device.id.uuidString { + Image(systemName: "star.fill") + .imageScale(.large).foregroundColor(.yellow) + .padding(.trailing) + } else { + Image(systemName: "circle.fill") + .imageScale(.large).foregroundColor(.gray) + .padding(.trailing) + } + VStack(alignment: .leading) { + Button(action: { + if UserDefaults.preferredPeripheralId.count > 0 && device.id.uuidString != UserDefaults.preferredPeripheralId { + if accessoryManager.allowDisconnect { + Task { try await accessoryManager.disconnect() } + } + presentingSwitchPreferredPeripheral = true + } else { + Task { + try? await accessoryManager.connect(to: device) + } + } + }) { + Text(device.name).font(.callout) + } + // Show transport type +#if !targetEnvironment(macCatalyst) + HStack(alignment: .center){ + TransportIcon(transportType: device.transportType) + if device.isManualConnection && (device.longName != nil || device.shortName != nil) { + VStack (alignment: .leading) { + Text("Last seen device:") + Text("\(String(describing: device))") + } + } + }.padding(.top, 3.0) +#else + //Different alignment for Mac + HStack(alignment: .firstTextBaseline){ + TransportIcon(transportType: device.transportType) + if device.isManualConnection && (device.longName != nil || device.shortName != nil) { + Text("Last seen device: \(String(describing: device))") + } + } +#endif + } + Spacer() + VStack { + device.getSignalStrength().map { + SignalStrengthIndicator(signalStrength: $0) + } + } + }.padding([.bottom, .top]) + .confirmationDialog("Connecting to a new radio will clear all app data on the phone.", isPresented: $presentingSwitchPreferredPeripheral, titleVisibility: .visible) { + Button("Connect to new radio?", role: .destructive) { + UserDefaults.preferredPeripheralId = device.id.uuidString + UserDefaults.preferredPeripheralNum = 0 + if accessoryManager.allowDisconnect { + Task { try await accessoryManager.disconnect() } + } + clearCoreDataDatabase(context: context, includeRoutes: false) + clearNotifications() + Task { + try await accessoryManager.connect(to: device) + } + } + } + } +} + diff --git a/Meshtastic/Views/Helpers/CompassView.swift b/Meshtastic/Views/Helpers/CompassView.swift new file mode 100644 index 00000000..1e58b224 --- /dev/null +++ b/Meshtastic/Views/Helpers/CompassView.swift @@ -0,0 +1,295 @@ +// +// CompassView.swift +// Meshtastic +// +// Created by Benjamin Faershtein on 11/14/25. +// + +import SwiftUI +import CoreLocation +import UIKit + +struct CompassView: View { + + /// Single waypoint parameter + let waypointLocation: CLLocationCoordinate2D? + + let waypointName: String? + + let color: Color + + @ObservedObject private var locationsHandler = LocationsHandler.shared + + // Haptic alignment tracking + private let alignmentTolerance: Double = 5.0 + @State private var inAlignment = false + + // Compute bearing from user → waypoint + private func bearingToWaypoint() -> Double? { + guard + let waypoint = waypointLocation, + let user = LocationsHandler.currentLocation + else { return nil } + + return BearingCalculator.bearingBetween( + userLocation: user, + waypoint: waypoint + ) + } + + // Trigger a vibration if aligned with waypoint + private func checkAlignment(bearing: Double,heading: Double) { + // Compute minimal angular difference between heading and bearing in [0, 180] + let rawDiff = abs(heading - bearing).truncatingRemainder(dividingBy: 360) + let diff = min(rawDiff, 360 - rawDiff) + + if diff <= alignmentTolerance { + if !inAlignment { + inAlignment = true + let generator = UIImpactFeedbackGenerator(style: .heavy) + generator.impactOccurred() + } + } else { + inAlignment = false + } + } + + + private func distanceToWaypoint() -> CLLocationDistance? { + guard + let waypoint = waypointLocation, + let user = LocationsHandler.currentLocation + else { return nil } + + let userLocation = CLLocation(latitude: user.latitude, longitude: user.longitude) + let waypointLocation = CLLocation(latitude: waypoint.latitude, longitude: waypoint.longitude) + + return userLocation.distance(from: waypointLocation) + } + + // Format distance with localization + private func formatDistance(_ distance: CLLocationDistance) -> String { + let measurement = Measurement(value: distance, unit: UnitLength.meters) + let formatter = MeasurementFormatter() + formatter.unitOptions = .naturalScale + formatter.numberFormatter.maximumFractionDigits = 2 + return formatter.string(from: measurement) + } + + + var body: some View { + NavigationStack { + VStack(spacing: 15) { + + VStack(spacing: 8) { + Text(waypointName ?? "Waypoint") + .font(.title2) + .bold() + .foregroundColor(color) + + if let wp = waypointLocation { + HStack{ + Image(systemName: "mappin.and.ellipse") + Text("\(String(format: "%.4f", wp.latitude)), \(String(format: "%.4f", wp.longitude))") + .font(.subheadline) + } + + if let distance = distanceToWaypoint() { + HStack{ + Image(systemName: "lines.measurement.horizontal") + Text("Distance: \(formatDistance(distance))") + .font(.subheadline) + .fontWeight(.semibold) + } + } + HStack { + Image(systemName: "location.north") + if let bearing = bearingToWaypoint() { + Text("Bearing: \(String(format: "%.0f°", bearing))") + .font(.subheadline) + } else { + Text("Bearing: N/A") + .font(.subheadline) + } + } + } + } + .padding() + + Capsule() + .frame(width: 5, height: 50) + ZStack { + + // Cardinal/degree markers + ForEach(Marker.markers(), id: \.self) { marker in + CompassMarkerView( + marker: marker, + compassDegrees: -locationsHandler.heading + ) + } + + // Waypoint bearing indicator + if let bearing = bearingToWaypoint() { + WaypointMarkerView( + bearing: bearing, + compassDegrees: locationsHandler.heading, + color: color + ) + // Move waypoint marker outside compass + .onChange(of: locationsHandler.heading) { _, _ in + checkAlignment(bearing: bearing,heading:locationsHandler.heading) + } + } + + } + .frame(width: 300, height: 300) + .rotationEffect(Angle(degrees: -locationsHandler.heading)) + .statusBar(hidden: true) + .onAppear { + locationsHandler.startHeadingUpdates() + locationsHandler.startLocationUpdates() + } + .onDisappear { + locationsHandler.stopHeadingUpdates() + locationsHandler.stopLocationUpdates() + } + .navigationTitle("Compass") + } + } + } +} + + +// MARK: - Waypoint Marker View + +struct WaypointMarkerView: View { + let bearing: Double + let compassDegrees: Double + let color: Color + + var body: some View { + Circle() + .frame(width: 20, height: 20) + .foregroundColor(color) + .offset(y: -170) + .rotationEffect(Angle(degrees: bearing)) + } + +} + + +// MARK: - Bearing Calculator + +struct BearingCalculator { + + static func bearingBetween( + userLocation: CLLocationCoordinate2D, + waypoint: CLLocationCoordinate2D + ) -> Double { + + let lat1 = userLocation.latitude * .pi / 180 + let lon1 = userLocation.longitude * .pi / 180 + let lat2 = waypoint.latitude * .pi / 180 + let lon2 = waypoint.longitude * .pi / 180 + + let dLon = lon2 - lon1 + + let y = sin(dLon) * cos(lat2) + let x = cos(lat1) * sin(lat2) + - sin(lat1) * cos(lat2) * cos(dLon) + + var bearing = atan2(y, x) * 180 / .pi + if bearing < 0 { bearing += 360 } + + return bearing + } +} + + +// MARK: - Marker Model + +struct Marker: Hashable { + let degrees: Double + let label: String + + init(degrees: Double, label: String = "") { + self.degrees = degrees + self.label = label + } + + func degreeText() -> String { + return String(format: "%.0f", self.degrees) + } + + static func markers() -> [Marker] { + return [ + Marker(degrees: 0, label: "N"), + Marker(degrees: 30), + Marker(degrees: 60), + Marker(degrees: 90, label: "E"), + Marker(degrees: 120), + Marker(degrees: 150), + Marker(degrees: 180, label: "S"), + Marker(degrees: 210), + Marker(degrees: 240), + Marker(degrees: 270, label: "W"), + Marker(degrees: 300), + Marker(degrees: 330) + ] + } +} + + +// MARK: - Compass Marker View + +struct CompassMarkerView: View { + let marker: Marker + let compassDegrees: Double + + var body: some View { + VStack { + Text(marker.degreeText()) + .fontWeight(.light) + .rotationEffect(textAngle()) + + Capsule() + .frame(width: capsuleWidth(), height: capsuleHeight()) + .foregroundColor(capsuleColor()) + + Text(marker.label) + .fontWeight(.bold) + .rotationEffect(textAngle()) + .padding(.bottom, 180) + } + .rotationEffect(Angle(degrees: marker.degrees)) + } + + private func capsuleWidth() -> CGFloat { + marker.degrees == 0 ? 7 : 3 + } + + private func capsuleHeight() -> CGFloat { + marker.degrees == 0 ? 45 : 30 + } + + private func capsuleColor() -> Color { + marker.degrees == 0 ? .red : .gray + } + + private func textAngle() -> Angle { + Angle(degrees: -compassDegrees - marker.degrees) + } +} + + +// MARK: - Preview + +struct CompassView_Previews: PreviewProvider { + static var previews: some View { + CompassView( + waypointLocation: CLLocationCoordinate2D(latitude: 37.3346, longitude: -122.0090), + waypointName: "Apple Park", + color: Color.orange + ) + } +} diff --git a/Meshtastic/Views/Helpers/ConnectedDevice.swift b/Meshtastic/Views/Helpers/ConnectedDevice.swift index 1cb46948..deb26509 100644 --- a/Meshtastic/Views/Helpers/ConnectedDevice.swift +++ b/Meshtastic/Views/Helpers/ConnectedDevice.swift @@ -1,22 +1,31 @@ /* -Abstract: -A view draws the indicator used in the upper right corner for views using BLE -*/ + Abstract: + A view draws the indicator used in the upper right corner for views using BLE + */ import SwiftUI struct ConnectedDevice: View { - @EnvironmentObject var accessoryManager: AccessoryManager - var deviceConnected: Bool - var name: String - var mqttProxyConnected: Bool = false - var mqttUplinkEnabled: Bool = false - var mqttDownlinkEnabled: Bool = false - var mqttTopic: String = "" - var phoneOnly: Bool = false - var showActivityLights: Bool - - init(deviceConnected: Bool, name: String, mqttProxyConnected: Bool = false, mqttUplinkEnabled: Bool = false, mqttDownlinkEnabled: Bool = false, mqttTopic: String = "", phoneOnly: Bool = false, showActivityLights: Bool = true) { + + let deviceConnected: Bool + let name: String + let mqttProxyConnected: Bool + let mqttUplinkEnabled: Bool + let mqttDownlinkEnabled: Bool + let mqttTopic: String + let phoneOnly: Bool + let showActivityLights: Bool + + init( + deviceConnected: Bool, + name: String, + mqttProxyConnected: Bool = false, + mqttUplinkEnabled: Bool = false, + mqttDownlinkEnabled: Bool = false, + mqttTopic: String = "", + phoneOnly: Bool = false, + showActivityLights: Bool = true + ) { self.deviceConnected = deviceConnected self.name = name self.mqttProxyConnected = mqttProxyConnected @@ -27,12 +36,12 @@ struct ConnectedDevice: View { self.showActivityLights = showActivityLights } - var body: some View { + var body: some View { HStack { if showActivityLights { RXTXIndicatorWidget() } - if (phoneOnly && UIDevice.current.userInterfaceIdiom == .phone) || !phoneOnly { + if (phoneOnly && UIDevice.current.userInterfaceIdiom == .phone) || !phoneOnly { if deviceConnected { // Create an HStack for connected state with proper accessibility HStack { @@ -64,24 +73,37 @@ struct ConnectedDevice: View { .accessibilityElement(children: .ignore) .accessibilityLabel("No Bluetooth device connected".localized) } - } + } } .if(.os26) { $0.padding(.leading, 5.0) } - } + } } -struct ConnectedDevice_Previews: PreviewProvider { - static var previews: some View { - VStack(alignment: .trailing) { - ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: true) - ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: false, mqttUplinkEnabled: true, mqttDownlinkEnabled: true) - ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: true, mqttUplinkEnabled: true, mqttDownlinkEnabled: true, mqttTopic: "msh/US/2/e/#") - ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: false, mqttUplinkEnabled: true, mqttDownlinkEnabled: false) - ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: true, mqttUplinkEnabled: true, mqttDownlinkEnabled: false) - ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: false, mqttUplinkEnabled: false, mqttDownlinkEnabled: true) - ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: true, mqttUplinkEnabled: false, mqttDownlinkEnabled: true) - ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: true) - ConnectedDevice(deviceConnected: false, name: "MEMO", mqttProxyConnected: false) - }.previewLayout(.fixed(width: 150, height: 275)) - } +#Preview("Multiple variants") { + VStack(alignment: .trailing) { + ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: true) + ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: false, mqttUplinkEnabled: true, mqttDownlinkEnabled: true) + ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: true, mqttUplinkEnabled: true, mqttDownlinkEnabled: true, mqttTopic: "msh/US/2/e/#") + ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: false, mqttUplinkEnabled: true, mqttDownlinkEnabled: false) + ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: true, mqttUplinkEnabled: true, mqttDownlinkEnabled: false) + ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: false, mqttUplinkEnabled: false, mqttDownlinkEnabled: true) + ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: true, mqttUplinkEnabled: false, mqttDownlinkEnabled: true) + ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: true) + ConnectedDevice(deviceConnected: false, name: "MEMO", mqttProxyConnected: false) + } + .environmentObject(AccessoryManager.shared) +} + +#Preview("Navigation header item") { + NavigationView { + Text("Connect screen") + .navigationTitle("Connect") + .navigationBarItems( + leading: MeshtasticLogo(), + trailing: ZStack { + ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: false) + .environmentObject(AccessoryManager.shared) + } + ) + } } diff --git a/Meshtastic/Views/Helpers/RXTXIndicatorView.swift b/Meshtastic/Views/Helpers/RXTXIndicatorView.swift index 25168718..860b3734 100644 --- a/Meshtastic/Views/Helpers/RXTXIndicatorView.swift +++ b/Meshtastic/Views/Helpers/RXTXIndicatorView.swift @@ -12,7 +12,7 @@ import OSLog struct RXTXIndicatorWidget: View { @EnvironmentObject var accessoryManager: AccessoryManager @State private var isPopoverOpen = false - + let fontSize: CGFloat = 7.0 var body: some View { Button( action: { @@ -38,7 +38,7 @@ struct RXTXIndicatorWidget: View { #else self.isPopoverOpen.toggle() #endif - + }) { VStack(spacing: 3.0) { HStack(spacing: 2.0) { @@ -102,9 +102,9 @@ struct LEDIndicator: View { @Environment(\.colorScheme) var colorScheme @Binding var flash: Int let color: Color - + @State private var brightness: Double = 0.0 - + var body: some View { Circle() .foregroundColor(color.opacity(brightness)) diff --git a/Meshtastic/Views/Messages/ChannelList.swift b/Meshtastic/Views/Messages/ChannelList.swift index 5b3af8f6..16660203 100644 --- a/Meshtastic/Views/Messages/ChannelList.swift +++ b/Meshtastic/Views/Messages/ChannelList.swift @@ -37,14 +37,16 @@ struct ChannelList: View { let dateFormatString = (localeDateFormat ?? "MM/dd/YY") NavigationLink(value: channel) { - let mostRecent = channel.allPrivateMessages.last(where: { $0.channel == channel.index }) + let mostRecent = channel.mostRecentPrivateMessage + let hasMessages = mostRecent != nil + let hasUnreadMessages = hasMessages && (channel.unreadMessages > 0) let lastMessageTime = Date(timeIntervalSince1970: TimeInterval(Int64((mostRecent?.messageTimestamp ?? 0 )))) let lastMessageDay = Calendar.current.dateComponents([.day], from: lastMessageTime).day ?? 0 let currentDay = Calendar.current.dateComponents([.day], from: Date()).day ?? 0 ZStack { Image(systemName: "circle.fill") - .opacity(channel.unreadMessages > 0 ? 1 : 0) + .opacity(hasUnreadMessages ? 1 : 0) .font(.system(size: 10)) .foregroundColor(.accentColor) .brightness(0.2) @@ -70,7 +72,7 @@ struct ChannelList: View { Spacer() - if channel.allPrivateMessages.count > 0 { + if hasMessages { if lastMessageDay == currentDay { Text(lastMessageTime, style: .time ) @@ -95,7 +97,7 @@ struct ChannelList: View { } } - if channel.allPrivateMessages.count > 0 { + if hasMessages { HStack(alignment: .top) { Text("\(mostRecent != nil ? mostRecent!.messagePayload! : " ")") // .font(.system(size: 16)) @@ -112,6 +114,7 @@ struct ChannelList: View { if let node, let myInfo = node.myInfo { List(selection: $channelSelection) { ForEach(channels) { (channel: ChannelEntity) in + let hasMessages = channel.mostRecentPrivateMessage != nil if !restrictedChannels.contains(channel.name?.lowercased() ?? "") { makeChannelRow(myInfo: myInfo, channel: channel) .alignmentGuide(.listRowSeparatorLeading) { @@ -119,7 +122,7 @@ struct ChannelList: View { } .frame(height: 62) .contextMenu { - if channel.allPrivateMessages.count > 0 { + if hasMessages { Button(role: .destructive) { isPresentingDeleteChannelMessagesConfirm = true channelToDeleteMessages = channel diff --git a/Meshtastic/Views/Messages/ChannelMessageList.swift b/Meshtastic/Views/Messages/ChannelMessageList.swift index 857028f1..be3959d2 100644 --- a/Meshtastic/Views/Messages/ChannelMessageList.swift +++ b/Meshtastic/Views/Messages/ChannelMessageList.swift @@ -12,7 +12,7 @@ import SwiftUI struct ChannelMessageList: View { @EnvironmentObject var appState: AppState - @EnvironmentObject var router: Router + @Environment(\.scenePhase) var scenePhase @Environment(\.managedObjectContext) var context @EnvironmentObject var accessoryManager: AccessoryManager @FocusState var messageFieldFocused: Bool @@ -58,14 +58,29 @@ struct ChannelMessageList: View { Logger.data.error("Failed to read messages: \(error.localizedDescription, privacy: .public)") } } - + + private func routerIsShowingThisChannel() -> Bool { + guard appState.router.navigationState.selectedTab == .messages else { return false } + return scenePhase == .active + } + var body: some View { + // Cast allPrivateMessages to an array for easier indexing and ForEach. + let messages: [MessageEntity] = Array(allPrivateMessages) + + // Precompute previous message + let previousByID: [Int64: MessageEntity?] = { + var dict = [Int64: MessageEntity?]() + var prev: MessageEntity? + for m in messages { dict[m.messageId] = prev; prev = m } + return dict + }() + ScrollViewReader { scrollView in ScrollView { LazyVStack { - ForEach(allPrivateMessages.indices, id: \.self) { index in - let message = allPrivateMessages[index] - let previousMessage = index > 0 ? allPrivateMessages[index - 1] : nil + ForEach(messages, id: \.messageId) { message in + let previousMessage: MessageEntity? = previousByID[message.messageId] ?? nil ChannelMessageRow( message: message, @@ -92,7 +107,7 @@ struct ChannelMessageList: View { } } } - .id(redrawTapbacksTrigger) + } Color.clear .frame(height: 1) diff --git a/Meshtastic/Views/Messages/ChannelMessageRow.swift b/Meshtastic/Views/Messages/ChannelMessageRow.swift index 9e3c8124..eb0f2a9f 100644 --- a/Meshtastic/Views/Messages/ChannelMessageRow.swift +++ b/Meshtastic/Views/Messages/ChannelMessageRow.swift @@ -3,7 +3,7 @@ import MeshtasticProtobufs import SwiftUI struct ChannelMessageRow: View { - @EnvironmentObject var router: Router + @EnvironmentObject var appState: AppState // Core Data object observed for changes (like Tapbacks being received) @ObservedObject var message: MessageEntity @@ -98,7 +98,7 @@ struct ChannelMessageRow: View { CircleText(text: message.fromUser?.shortName ?? "?", color: Color(UIColor(hex: UInt32(message.fromUser?.num ?? 0))), circleSize: 50) .onTapGesture(count: 2) { if let nodeNum = message.fromUser?.num { - router.navigateToNodeDetail(nodeNum: Int64(nodeNum)) + appState.router.navigateToNodeDetail(nodeNum: Int64(nodeNum)) } } .padding(.all, 5).offset(y: -7) diff --git a/Meshtastic/Views/Messages/MessageContextMenuItems.swift b/Meshtastic/Views/Messages/MessageContextMenuItems.swift index 33554de7..63104320 100644 --- a/Meshtastic/Views/Messages/MessageContextMenuItems.swift +++ b/Meshtastic/Views/Messages/MessageContextMenuItems.swift @@ -11,6 +11,7 @@ struct MessageContextMenuItems: View { let isCurrentUser: Bool @Binding var isShowingDeleteConfirmation: Bool let onReply: () -> Void + @State var relayDisplay: String? = nil var body: some View { VStack { @@ -19,6 +20,14 @@ struct MessageContextMenuItems: View { } Text("Channel") + Text(": \(message.channel)") } + .onAppear { + DispatchQueue.global(qos: .userInitiated).async { + let result = message.relayDisplay() + DispatchQueue.main.async { + relayDisplay = result + } + } + } Menu("Tapback") { ForEach(Tapbacks.allCases) { tb in @@ -59,12 +68,27 @@ struct MessageContextMenuItems: View { } Menu("Message Details") { + // Precompute values to avoid executing non-View code inside the ViewBuilder + let messageDate = Date(timeIntervalSince1970: TimeInterval(message.messageTimestamp)) + let ackDate = Date(timeIntervalSince1970: TimeInterval(message.ackTimestamp)) + let sixMonthsAgo = Calendar.current.date(byAdding: .month, value: -6, to: Date()) + + // Compute a relay display string if relayNode is present + + VStack { - let messageDate = Date(timeIntervalSince1970: TimeInterval(message.messageTimestamp)) - Text("\(messageDate.formattedDate(format: MessageText.dateFormatString))").foregroundColor(.gray) + Text("\(messageDate.formattedDate(format: MessageText.dateFormatString))") + .foregroundColor(.gray) } - if !isCurrentUser && !(message.fromUser?.userNode?.viaMqtt ?? false) && message.fromUser?.userNode?.hopsAway ?? -1 == 0 { + if let relayDisplay { + let prefix = message.realACK ? "Ack Relay: " : "Relay: " + Text(prefix + relayDisplay) + .foregroundColor(relayDisplay.contains("Node ") ? .gray : .primary) + .font(relayDisplay.contains("Node ") ? .caption : .body) + } + + if !isCurrentUser && !(message.fromUser?.userNode?.viaMqtt ?? false) && message.fromUser?.userNode?.hopsAway ?? -1 == 0 { VStack { Text("SNR \(String(format: "%.2f", message.snr)) dB") Text("RSSI \(String(format: "%.2f", message.rssi)) dBm") @@ -74,29 +98,29 @@ struct MessageContextMenuItems: View { Text("Hops Away \(message.fromUser?.userNode?.hopsAway ?? 0)") } } + if message.relays != 0 && message.realACK == false { + Text("Relayed by \(message.relays) \(message.relays == 1 ? "node" : "nodes")") + } if isCurrentUser && message.receivedACK { VStack { - Text("Received Ack") + Text(": \(message.receivedACK ? "✔️" : "")") - Text("Recipient Ack") + Text(": \(message.realACK ? "✔️" : "")") + Text("Received Ack: \(message.receivedACK ? "✔️" : "")") + Text("Recipient Ack: \(message.realACK ? "✔️" : "")") } } else if isCurrentUser && message.ackError == 0 { - // Empty Error Text("Waiting") } else if isCurrentUser && message.ackError > 0 { let ackErrorVal = RoutingError(rawValue: Int(message.ackError)) Text("\(ackErrorVal?.display ?? "Empty Ack Error")") .fixedSize(horizontal: false, vertical: true) } + if isCurrentUser { - VStack { - let ackDate = Date(timeIntervalSince1970: TimeInterval(message.ackTimestamp)) - let sixMonthsAgo = Calendar.current.date(byAdding: .month, value: -6, to: Date()) - if ackDate >= sixMonthsAgo! { - Text("Ack Time: \(ackDate.formattedDate(format: MessageText.timeFormatString))") - .foregroundColor(.gray) - } + if let sixMonthsAgo, ackDate >= sixMonthsAgo { + Text("Ack Time: \(ackDate.formattedDate(format: MessageText.timeFormatString))") + .foregroundColor(.gray) } } + if message.ackSNR != 0 { VStack { Text("Ack SNR: \(String(format: "%.2f", message.ackSNR)) dB") diff --git a/Meshtastic/Views/Messages/MessageText.swift b/Meshtastic/Views/Messages/MessageText.swift index 19d0c715..28df8fba 100644 --- a/Meshtastic/Views/Messages/MessageText.swift +++ b/Meshtastic/Views/Messages/MessageText.swift @@ -25,9 +25,7 @@ struct MessageText: View { let isCurrentUser: Bool let onReply: () -> Void // State for handling channel URL sheet - @State private var saveChannels = false - @State private var channelSettings: String? - @State private var addChannels = false + @State private var saveChannelLink: SaveChannelLinkData? @State private var isShowingDeleteConfirmation = false var body: some View { @@ -97,7 +95,8 @@ struct MessageText: View { ) } .environment(\.openURL, OpenURLAction { url in - channelSettings = nil + saveChannelLink = nil + var addChannels = false if url.absoluteString.lowercased().contains("meshtastic.org/v/#") { // Handle contact URL ContactURLHandler.handleContactUrl(url: url, accessoryManager: AccessoryManager.shared) @@ -109,35 +108,25 @@ struct MessageText: View { Logger.services.error("No valid components found in channel URL: \(url.absoluteString, privacy: .public)") return .discarded } - self.addChannels = Bool(url.query?.contains("add=true") ?? false) + addChannels = Bool(url.query?.contains("add=true") ?? false) guard let lastComponent = components.last else { Logger.services.error("Channel URL missing fragment component: \(url.absoluteString, privacy: .public)") - self.channelSettings = nil + self.saveChannelLink = nil return .discarded } - self.channelSettings = lastComponent.components(separatedBy: "?").first ?? "" - Logger.services.debug("Add Channel: \(self.addChannels, privacy: .public)") - self.saveChannels = true + let cs = lastComponent.components(separatedBy: "?").first ?? "" + self.saveChannelLink = SaveChannelLinkData(data: cs, add: addChannels) + Logger.services.debug("Add Channel: \(addChannels, privacy: .public)") Logger.mesh.debug("Opening Channel Settings URL: \(url.absoluteString, privacy: .public)") return .handled // Prevent default browser opening } return .systemAction // Open other URLs in browser }) // Display sheet for channel settings - .sheet(isPresented: Binding( - get: { - saveChannels && !(channelSettings == nil) - }, - set: { newValue in - saveChannels = newValue - if !newValue { - channelSettings = nil - } - } - )) { + .sheet(item: $saveChannelLink) { link in SaveChannelQRCode( - channelSetLink: channelSettings ?? "Empty Channel URL", - addChannels: addChannels, + channelSetLink: link.data, + addChannels: link.add, accessoryManager: accessoryManager ) .presentationDetents([.large]) diff --git a/Meshtastic/Views/Messages/UserList.swift b/Meshtastic/Views/Messages/UserList.swift index 2199e997..16ba0b7b 100644 --- a/Meshtastic/Views/Messages/UserList.swift +++ b/Meshtastic/Views/Messages/UserList.swift @@ -11,7 +11,7 @@ import OSLog import TipKit struct UserList: View { - + @Environment(\.managedObjectContext) var context @EnvironmentObject var accessoryManager: AccessoryManager @State private var editingFilters = false @@ -20,7 +20,7 @@ struct UserList: View { @StateObject private var filters: NodeFilterParameters = NodeFilterParameters() @Binding var node: NodeInfoEntity? @Binding var userSelection: UserEntity? - + var body: some View { VStack { FilteredUserList(withFilters: filters, node: $node, userSelection: $userSelection) @@ -92,13 +92,15 @@ fileprivate struct FilteredUserList: View { self._node = node self._userSelection = userSelection } - + var body: some View { let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMdd", options: 0, locale: Locale.current) let dateFormatString = (localeDateFormat ?? "MM/dd/YY") List(users, selection: $userSelection) { user in - let mostRecent = user.messageList.last + let mostRecent = user.mostRecentMessage + let hasMessages = mostRecent != nil + let hasUnreadMessages = user.unreadMessages > 0 let lastMessageTime = Date(timeIntervalSince1970: TimeInterval(Int64((mostRecent?.messageTimestamp ?? 0 )))) let lastMessageDay = Calendar.current.dateComponents([.day], from: lastMessageTime).day ?? 0 let currentDay = Calendar.current.dateComponents([.day], from: Date()).day ?? 0 @@ -106,14 +108,14 @@ fileprivate struct FilteredUserList: View { NavigationLink(value: user) { ZStack { Image(systemName: "circle.fill") - .opacity(user.unreadMessages > 0 ? 1 : 0) + .opacity(hasUnreadMessages ? 1 : 0) .font(.system(size: 10)) .foregroundColor(.accentColor) .brightness(0.2) } - + CircleText(text: user.shortName ?? "?", color: Color(UIColor(hex: UInt32(user.num)))) - + VStack(alignment: .leading) { HStack { if user.pkiEncrypted { @@ -137,7 +139,7 @@ fileprivate struct FilteredUserList: View { Image(systemName: "star.fill") .foregroundColor(.yellow) } - if user.messageList.count > 0 { + if hasMessages { if lastMessageDay == currentDay { Text(lastMessageTime, style: .time ) .font(.footnote) @@ -157,8 +159,8 @@ fileprivate struct FilteredUserList: View { } } } - - if user.messageList.count > 0 { + + if hasMessages { HStack(alignment: .top) { Text("\(mostRecent != nil ? mostRecent!.messagePayload! : " ")") .font(.footnote) @@ -207,7 +209,7 @@ fileprivate struct FilteredUserList: View { } label: { Label(user.mute ? "Show Alerts" : "Hide Alerts", systemImage: user.mute ? "bell" : "bell.slash") } - if user.messageList.count > 0 { + if hasMessages { Button(role: .destructive) { isPresentingDeleteUserMessagesConfirm = true userToDeleteMessages = user @@ -316,7 +318,7 @@ fileprivate extension NodeFilterParameters { predicates.append(isIgnoredPredicate) let isConnectedNodePredicate = NSPredicate(format: "NOT (numString CONTAINS %@)", String(UserDefaults.preferredPeripheralNum)) predicates.append(isConnectedNodePredicate) - + // Combine all predicates let finalPredicate = predicates.isEmpty ? NSPredicate(value: true) : NSCompoundPredicate(type: .and, subpredicates: predicates) return finalPredicate diff --git a/Meshtastic/Views/Messages/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index 87afb16f..9a3425bc 100644 --- a/Meshtastic/Views/Messages/UserMessageList.swift +++ b/Meshtastic/Views/Messages/UserMessageList.swift @@ -11,9 +11,9 @@ import OSLog import MeshtasticProtobufs // Added to ensure RoutingError is accessible if needed struct UserMessageList: View { - @EnvironmentObject var appState: AppState @EnvironmentObject var accessoryManager: AccessoryManager + @Environment(\.scenePhase) var scenePhase @Environment(\.managedObjectContext) var context @FocusState var messageFieldFocused: Bool @ObservedObject var user: UserEntity @@ -21,17 +21,21 @@ struct UserMessageList: View { @State private var messageToHighlight: Int64 = 0 @State private var redrawTapbacksTrigger = UUID() @AppStorage("preferredPeripheralNum") private var preferredPeripheralNum = -1 - - private var allPrivateMessages: [MessageEntity] { - // Cast user.messageList to an array for easier indexing and ForEach. - return user.messageList.compactMap { $0 as MessageEntity } + @FetchRequest private var allPrivateMessages: FetchedResults + + init(user: UserEntity) { + self.user = user + + // Configure fetch request here + let request: NSFetchRequest = user.messageFetchRequest + _allPrivateMessages = FetchRequest(fetchRequest: request) } - + func handleInteractionComplete() { markMessagesAsRead() redrawTapbacksTrigger = UUID() } - + func markMessagesAsRead() { do { for unreadMessage in allPrivateMessages.filter({ !$0.read }) { @@ -39,25 +43,46 @@ struct UserMessageList: View { } try context.save() Logger.data.info("📖 [App] All unread direct messages marked as read for user \(user.num, privacy: .public).") - appState.unreadDirectMessages = user.unreadMessages + + if let connectedPeripheralNum = accessoryManager.activeDeviceNum, + let connectedNode = getNodeInfo(id: connectedPeripheralNum, context: context), + let connectedUser = connectedNode.user { + appState.unreadDirectMessages = connectedUser.unreadMessages(context: context, skipLastMessageCheck: true) // skipLastMessageCheck=true because we don't update lastMessage on our own connected node + } + context.refresh(user, mergeChanges: true) } catch { Logger.data.error("Failed to read direct messages: \(error.localizedDescription, privacy: .public)") } } - + + private func routerIsShowingThisUser() -> Bool { + guard appState.router.navigationState.selectedTab == .messages else { return false } + return scenePhase == .active + } + var body: some View { + // Cast user.messageList to an array for easier indexing and ForEach. + let messages: [MessageEntity] = Array(allPrivateMessages) + + // Precompute previous message + let previousByID: [Int64: MessageEntity?] = { + var dict = [Int64: MessageEntity?]() + var prev: MessageEntity? + for m in messages { dict[m.messageId] = prev; prev = m } + return dict + }() + VStack { ScrollViewReader { scrollView in ScrollView { LazyVStack { - ForEach(allPrivateMessages.indices, id: \.self) { index in - let message = allPrivateMessages[index] - let previousMessage = index > 0 ? allPrivateMessages[index - 1] : nil + ForEach(messages, id: \.messageId) { message in + let previousMessage: MessageEntity? = previousByID[message.messageId] ?? nil UserMessageRow( message: message, - allMessages: allPrivateMessages, + allMessages: messages, previousMessage: previousMessage, preferredPeripheralNum: preferredPeripheralNum, user: user, @@ -80,7 +105,7 @@ struct UserMessageList: View { } } } - .id(redrawTapbacksTrigger) + } // Invisible spacer to detect reaching bottom Color.clear diff --git a/Meshtastic/Views/Messages/UserMessageRow.swift b/Meshtastic/Views/Messages/UserMessageRow.swift index 00579fb3..d469462b 100644 --- a/Meshtastic/Views/Messages/UserMessageRow.swift +++ b/Meshtastic/Views/Messages/UserMessageRow.swift @@ -11,7 +11,7 @@ import SwiftUI struct UserMessageRow: View { - @EnvironmentObject var router: Router + @EnvironmentObject var appState: AppState @ObservedObject var message: MessageEntity let allMessages: [MessageEntity] let previousMessage: MessageEntity? @@ -105,7 +105,7 @@ struct UserMessageRow: View { CircleText(text: message.fromUser?.shortName ?? "?", color: Color(UIColor(hex: UInt32(message.fromUser?.num ?? 0))), circleSize: 50) .onTapGesture(count: 2) { if let nodeNum = message.fromUser?.num { - router.navigateToNodeDetail(nodeNum: Int64(nodeNum)) + appState.router.navigateToNodeDetail(nodeNum: Int64(nodeNum)) } } .padding(.all, 5).offset(y: -7) diff --git a/Meshtastic/Views/Nodes/Helpers/Actions/ExchangePositionsButton.swift b/Meshtastic/Views/Nodes/Helpers/Actions/ExchangePositionsButton.swift index 05747fa9..f303f21e 100644 --- a/Meshtastic/Views/Nodes/Helpers/Actions/ExchangePositionsButton.swift +++ b/Meshtastic/Views/Nodes/Helpers/Actions/ExchangePositionsButton.swift @@ -3,6 +3,7 @@ import SwiftUI struct ExchangePositionsButton: View { var node: NodeInfoEntity + var connectedNode: NodeInfoEntity @EnvironmentObject var accessoryManager: AccessoryManager @@ -10,7 +11,7 @@ struct ExchangePositionsButton: View { @State private var isPresentingPositionFailedAlert: Bool = false var body: some View { - let hopsAway = Int32(truncatingIfNeeded: node.hopsAway > node.loRaConfig?.hopLimit ?? 0 ? node.hopsAway : node.loRaConfig?.hopLimit ?? 0) + let hopsAway = Int32(truncatingIfNeeded: node.hopsAway > connectedNode.loRaConfig?.hopLimit ?? 0 ? node.hopsAway : connectedNode.loRaConfig?.hopLimit ?? 0) Button { Task { do { diff --git a/Meshtastic/Views/Nodes/Helpers/Actions/FavoriteNodeButton.swift b/Meshtastic/Views/Nodes/Helpers/Actions/FavoriteNodeButton.swift index 4ca8009b..83bac1d3 100644 --- a/Meshtastic/Views/Nodes/Helpers/Actions/FavoriteNodeButton.swift +++ b/Meshtastic/Views/Nodes/Helpers/Actions/FavoriteNodeButton.swift @@ -8,39 +8,20 @@ struct FavoriteNodeButton: View { @Environment(\.managedObjectContext) var context @ObservedObject var node: NodeInfoEntity + @State var isShowingClientBaseConfirmation = false var body: some View { + let connectedRoleIsClientBase = accessoryManager.connectedDeviceRole == DeviceRoles.clientBase Button { + // Special case for CLIENT_BASE: show confirmation when attempting to favorite a node + if connectedRoleIsClientBase && !node.favorite { + isShowingClientBaseConfirmation = true + return + } + // Normal case: perform action immediately guard let connectedNodeNum = accessoryManager.activeDeviceNum else { return } Task { - do { - if node.favorite { - try await accessoryManager.removeFavoriteNode( - node: node, - connectedNodeNum: Int64(connectedNodeNum) - ) - } else { - try await accessoryManager.setFavoriteNode( - node: node, - connectedNodeNum: Int64(connectedNodeNum) - ) - } - - Task { @MainActor in - // Update CoreData - node.favorite = !node.favorite - - do { - try context.save() - } catch { - context.rollback() - Logger.data.error("Save Node Favorite Error") - } - Logger.data.debug("Favorited a node") - } - } catch { - - } + await assignFavorite(node: node, setToFavorite: !node.favorite, connectedNodeNum: Int64(connectedNodeNum)) } } label: { Label { @@ -50,5 +31,51 @@ struct FavoriteNodeButton: View { .symbolRenderingMode(.multicolor) } } + .confirmationDialog( + "Are you sure?", + isPresented: $isShowingClientBaseConfirmation, + titleVisibility: .visible + ) { + Button("Yes, I control this node") { + guard let connectedNodeNum = accessoryManager.activeDeviceNum else { return } + Task { + await assignFavorite(node: node, setToFavorite: true, connectedNodeNum: Int64(connectedNodeNum)) + } + } + Button("Cancel", role: .cancel) { } + } message: { + Text("Client Base should only favorite other nodes you control. Improper use will hurt your local mesh.") + } + } + + private func assignFavorite (node: NodeInfoEntity, setToFavorite: Bool, connectedNodeNum: Int64) async { + do { + if setToFavorite { + try await accessoryManager.setFavoriteNode( + node: node, + connectedNodeNum: Int64(connectedNodeNum) + ) + } else { + try await accessoryManager.removeFavoriteNode( + node: node, + connectedNodeNum: Int64(connectedNodeNum) + ) + } + + Task { @MainActor in + // Update CoreData + node.favorite = setToFavorite + + do { + try context.save() + } catch { + context.rollback() + Logger.data.error("Save Node Favorite Error") + } + Logger.data.debug("Favorited a node") + } + } catch { + + } } } diff --git a/Meshtastic/Views/Nodes/Helpers/Map/MapContent/MeshMapContent.swift b/Meshtastic/Views/Nodes/Helpers/Map/MapContent/MeshMapContent.swift index 4941dd30..480a5cba 100644 --- a/Meshtastic/Views/Nodes/Helpers/Map/MapContent/MeshMapContent.swift +++ b/Meshtastic/Views/Nodes/Helpers/Map/MapContent/MeshMapContent.swift @@ -15,6 +15,12 @@ struct IdentifiableOverlay: Identifiable { var id: ObjectIdentifier { ObjectIdentifier(overlay as AnyObject) } } +struct ReducedPrecisionMapCircleKey: Hashable { + let latitudeI: Int32 + let longitudeI: Int32 + let precisionBits: Int32 +} + struct MeshMapContent: MapContent { /// Parameters @@ -43,35 +49,85 @@ struct MeshMapContent: MapContent { @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "name", ascending: true)], predicate: NSPredicate(format: "enabled == true", ""), animation: .none) private var routes: FetchedResults - + @MapContentBuilder var positionAnnotations: some MapContent { ForEach(positions, id: \.id) { position in + /// Apply favorites filter and don't show ignored nodes if (!showFavorites || (position.nodePosition?.favorite == true)) && !(position.nodePosition?.ignored == true) { - - let nodeColor = UIColor(hex: UInt32(position.nodePosition?.num ?? 0)) - let positionName = position.nodePosition?.user?.longName ?? "?" - // Use a hash of the position ID to stagger animation delays for each node, preventing synchronized animations and improving visual distinction. - let calculatedDelay = Double(position.id.hashValue % 100) / 100.0 * 0.5 - - Annotation(positionName, coordinate: position.coordinate) { - LazyVStack { - AnimatedNodePin( - nodeColor: nodeColor, - shortName: position.nodePosition?.user?.shortName, - hasDetectionSensorMetrics: position.nodePosition?.hasDetectionSensorMetrics ?? false, - isOnline: position.nodePosition?.isOnline ?? false, - calculatedDelay: calculatedDelay - ) + let coordinateForNodePin: CLLocationCoordinate2D = if position.isPreciseLocation { + // Precise location: place node pin at actual location. + position.coordinate + } else { + // Imprecise location: fuzz slightly so overlapping nodes are visible and clickable at highest zoom levels. + position.fuzzedCoordinate + } + if 12...15 ~= position.precisionBits || position.precisionBits == 32 { + + let nodeColor = UIColor(hex: UInt32(position.nodePosition?.num ?? 0)) + let positionName = position.nodePosition?.user?.longName ?? "?" + + // Use a hash of the position ID to stagger animation delays for each node, preventing synchronized animations and improving visual distinction. + let calculatedDelay = Double(position.id.hashValue % 100) / 100.0 * 0.5 + + Annotation(positionName, coordinate: coordinateForNodePin) { + LazyVStack { + AnimatedNodePin( + nodeColor: nodeColor, + shortName: position.nodePosition?.user?.shortName, + hasDetectionSensorMetrics: position.nodePosition?.hasDetectionSensorMetrics ?? false, + isOnline: position.nodePosition?.isOnline ?? false, + calculatedDelay: calculatedDelay + ) + } + .highPriorityGesture(TapGesture().onEnded { _ in + selectedPosition = (selectedPosition == position ? nil : position) + }) } - .highPriorityGesture(TapGesture().onEnded { _ in - selectedPosition = (selectedPosition == position ? nil : position) - }) } } } } - + + private var reducedPrecisionCircleItems: [(nodeNum: Int64, circleKey: ReducedPrecisionMapCircleKey)] { + // Precompute *unique* reduced-precision circles so we don't have to redraw tons of identical (center, radius) circles in dense map areas. (Since they're all transparent, this causes severe FPS drop when zoomed into areas where there are a ton of overlapping circles.) + var lowestNumForKey: [ReducedPrecisionMapCircleKey: Int64] = [:] + // Populate a dict where the key is (lat, lon, bits) and the value is the *lowest* node.num seen for that key. + // That lowest node.num value is used to create a stable color for the MapCircle and stable id for ForEach. + for position in positions { + // Same filter criteria as positionAnnotations: + if (!showFavorites || (position.nodePosition?.favorite == true)) && !(position.nodePosition?.ignored == true) { + if 12...15 ~= position.precisionBits { + let nodeNum = position.nodePosition?.num ?? 0 + let key = ReducedPrecisionMapCircleKey(latitudeI: position.latitudeI, longitudeI: position.longitudeI, precisionBits: position.precisionBits) + if let existing = lowestNumForKey[key] { + if nodeNum < existing { lowestNumForKey[key] = nodeNum } + } else { + lowestNumForKey[key] = nodeNum + } + } + } + } + // Sort by nodeNum just to keep draw order stable. + return lowestNumForKey.map { ($0.value, $0.key) }.sorted { $0.nodeNum < $1.nodeNum } + } + + @MapContentBuilder + var reducedPrecisionMapCircles: some MapContent { + ForEach(reducedPrecisionCircleItems, id: \.nodeNum) { item in + let circleKey = item.circleKey + let nodeNum = item.nodeNum + let radius = PositionPrecision(rawValue: Int(circleKey.precisionBits))?.precisionMeters ?? 0 + if radius > 0.0 { + let center = CLLocationCoordinate2D(latitude: Double(circleKey.latitudeI) / 1e7, longitude: Double(circleKey.longitudeI) / 1e7) + let nodeColor = UIColor(hex: UInt32(nodeNum)) + MapCircle(center: center, radius: radius) + .foregroundStyle(Color(nodeColor).opacity(0.25)) + .stroke(.white, lineWidth: 1) + } + } + } + @MapContentBuilder var routeAnnotations: some MapContent { ForEach(routes) { route in @@ -147,6 +203,7 @@ struct MeshMapContent: MapContent { } positionAnnotations + reducedPrecisionMapCircles routeAnnotations waypointAnnotations } diff --git a/Meshtastic/Views/Nodes/Helpers/Map/MapContent/NodeMapContent.swift b/Meshtastic/Views/Nodes/Helpers/Map/MapContent/NodeMapContent.swift index 6059a57c..02635dd8 100644 --- a/Meshtastic/Views/Nodes/Helpers/Map/MapContent/NodeMapContent.swift +++ b/Meshtastic/Views/Nodes/Helpers/Map/MapContent/NodeMapContent.swift @@ -11,42 +11,32 @@ import CoreData struct NodeMapContent: MapContent { @ObservedObject var node: NodeInfoEntity - @State var showUserLocation: Bool = false - @State var positions: [PositionEntity] = [] /// Map State User Defaults @AppStorage("meshMapShowNodeHistory") private var showNodeHistory = false @AppStorage("meshMapShowRouteLines") private var showRouteLines = false - @AppStorage("enableMapWaypoints") private var showWaypoints = true @AppStorage("enableMapConvexHull") private var showConvexHull = false - @AppStorage("enableMapTraffic") private var showTraffic: Bool = false - @AppStorage("enableMapPointsOfInterest") private var showPointsOfInterest: Bool = false - @AppStorage("mapLayer") private var selectedMapLayer: MapLayer = .hybrid // Map Configuration @Namespace var mapScope - @State var mapStyle: MapStyle = MapStyle.hybrid(elevation: .realistic, pointsOfInterest: .all, showsTraffic: true) - @State var position = MapCameraPosition.automatic - @State var scene: MKLookAroundScene? - @State var isLookingAround = false - @State var isShowingAltitude = false - @State var isEditingSettings = false @State var selectedPosition: PositionEntity? - @State var isMeshMap = false @MapContentBuilder var nodeMap: some MapContent { let positionArray = node.positions?.array as? [PositionEntity] ?? [] - let lineCoords = positionArray.compactMap({(position) -> CLLocationCoordinate2D in - return position.nodeCoordinate ?? LocationsHandler.DefaultLocation - }) /// Node Color from node.num let nodeColor = UIColor(hex: UInt32(node.num)) + let nodeColorSwift = Color(nodeColor) + let nodeBorderColor: Color = nodeColorSwift.isLight() ? .black : .white + + // Prerender node history point views as UIImages for speedup when there are thousands of history points + let prerenderedHistoryPointCircleImage = showNodeHistory ? prerenderHistoryPointCircle(fill: nodeColorSwift, stroke: nodeBorderColor) : UIImage() + let prerenderedHistoryPointArrowImage = showNodeHistory ? prerenderHistoryPointArrow(fill: nodeColorSwift, stroke: nodeBorderColor) : UIImage() + + let pf = PositionFlags(rawValue: Int(node.metadata?.positionFlags ?? 771)) /// Node Annotations - ForEach(node.positions?.array as? [PositionEntity] ?? [], id: \.id) { position in - - let pf = PositionFlags(rawValue: Int(position.nodePosition?.metadata?.positionFlags ?? 771)) + ForEach(positionArray, id: \.id) { position in let headingDegrees = Angle.degrees(Double(position.heading)) /// Reduced Precision Map Circle if position.latest && 12...15 ~= position.precisionBits { @@ -58,32 +48,6 @@ struct NodeMapContent: MapContent { .stroke(.white, lineWidth: 2) } } - let loraNodes = positions.filter { $0.nodePosition?.viaMqtt ?? true == false } - let loraCoords = Array(loraNodes).compactMap({(position) -> CLLocationCoordinate2D in - return position.nodeCoordinate ?? LocationsHandler.DefaultLocation - }) - /// Convex Hull - if showConvexHull { - if loraCoords.count > 0 { - let hull = loraCoords.getConvexHull() - MapPolygon(coordinates: hull) - .stroke(.blue, lineWidth: 3) - .foregroundStyle(.indigo.opacity(0.4)) - } - } - /// Route Lines - if showRouteLines { - let gradient = LinearGradient( - colors: [Color(nodeColor.lighter().lighter().lighter()), Color(nodeColor.lighter()), Color(nodeColor)], - startPoint: .leading, endPoint: .trailing - ) - let dashed = StrokeStyle( - lineWidth: 3, - lineCap: .round, lineJoin: .round, dash: [10, 10] - ) - MapPolyline(coordinates: lineCoords) - .stroke(gradient, style: dashed) - } /// Lastest Position Pin if position.latest { /// Node Annotations @@ -93,7 +57,7 @@ struct NodeMapContent: MapContent { if pf.contains(.Heading) { Image(systemName: pf.contains(.Speed) && position.speed > 1 ? "location.north" : "octagon") .padding(5) - .foregroundStyle(Color(nodeColor).isLight() ? .black : .white) + .foregroundStyle(nodeBorderColor) .background(Color(nodeColor.darker())) .clipShape(Circle()) .rotationEffect(headingDegrees) @@ -111,7 +75,7 @@ struct NodeMapContent: MapContent { Image(systemName: "flipphone") .symbolEffect(.pulse.byLayer) .padding(5) - .foregroundStyle(Color(nodeColor).isLight() ? .black : .white) + .foregroundStyle(nodeBorderColor) .background(Color(UIColor(hex: UInt32(node.num)).darker())) .clipShape(Circle()) .onTapGesture { @@ -133,27 +97,25 @@ struct NodeMapContent: MapContent { } /// Node History if showNodeHistory { - if position.latest == false && position.nodePosition?.favorite ?? false { - let pf = PositionFlags(rawValue: Int(position.nodePosition?.metadata?.positionFlags ?? 771)) + // Having showNodeHistory enabled can be quite slow if there are thousands of history points. + if position.latest == false && node.favorite { let headingDegrees = Angle.degrees(Double(position.heading)) Annotation("", coordinate: position.coordinate) { - LazyVStack { - if pf.contains(.Heading) { - Image(systemName: "location.north.circle") - .resizable() - .scaledToFit() - .foregroundStyle(Color(UIColor(hex: UInt32(position.nodePosition?.num ?? 0))).isLight() ? .black : .white) - .background(Color(UIColor(hex: UInt32(position.nodePosition?.num ?? 0)))) - .clipShape(Circle()) - .rotationEffect(headingDegrees) - .frame(width: 16, height: 16) - - } else { - Circle() - .fill(Color(UIColor(hex: UInt32(position.nodePosition?.num ?? 0)))) - .strokeBorder(Color(UIColor(hex: UInt32(position.nodePosition?.num ?? 0))).isLight() ? .black : .white, lineWidth: 2) - .frame(width: 12, height: 12) - } + if pf.contains(.Heading) { + Image(uiImage: prerenderedHistoryPointArrowImage) + .renderingMode(.original) + .interpolation(.none) + .rotationEffect(headingDegrees) + .frame(width: 16, height: 16) + .allowsHitTesting(false) + .accessibilityHidden(true) + } else { + Image(uiImage: prerenderedHistoryPointCircleImage) + .renderingMode(.original) + .interpolation(.none) + .frame(width: 12, height: 12) + .allowsHitTesting(false) + .accessibilityHidden(true) } } .annotationTitles(.hidden) @@ -161,6 +123,33 @@ struct NodeMapContent: MapContent { } } } + + // Shared coordinate list for Route Lines and Convex Hull + let allCoords: [CLLocationCoordinate2D] = (showRouteLines || showConvexHull) ? positionArray.compactMap(\.nodeCoordinate) : [] + + /// Route Lines + if showRouteLines { + let gradient = LinearGradient( + colors: [Color(nodeColor.lighter().lighter().lighter()), Color(nodeColor.lighter()), Color(nodeColor)], + startPoint: .leading, endPoint: .trailing + ) + let dashed = StrokeStyle( + lineWidth: 3, + lineCap: .round, lineJoin: .round, dash: [10, 10] + ) + MapPolyline(coordinates: allCoords) + .stroke(gradient, style: dashed) + } + + /// Convex Hull + if showConvexHull { + if allCoords.count > 0 { + let hull = allCoords.getConvexHull() + MapPolygon(coordinates: hull) + .stroke(.blue, lineWidth: 3) + .foregroundStyle(.indigo.opacity(0.4)) + } + } } @MapContentBuilder @@ -169,4 +158,29 @@ struct NodeMapContent: MapContent { nodeMap } } + + private func prerenderHistoryPointCircle(fill: Color, stroke: Color) -> UIImage { + // Render to UIImage once so we don't have to do a ton of vector operations and layers when there are thousands of history points. + let content = Circle() + .fill(fill) + .strokeBorder(stroke, lineWidth: 2) + .frame(width: 12, height: 12) + let renderer = ImageRenderer(content: content) + renderer.scale = UIScreen.main.scale + return renderer.uiImage! + } + + private func prerenderHistoryPointArrow(fill: Color, stroke: Color) -> UIImage { + // Render to UIImage once so we don't have to do a ton of vector operations and layers when there are thousands of history points. + let content = Image(systemName: "location.north.circle") + .resizable() + .scaledToFit() + .foregroundStyle(stroke) + .background(fill) + .clipShape(Circle()) + .frame(width: 16, height: 16) + let renderer = ImageRenderer(content: content) + renderer.scale = UIScreen.main.scale + return renderer.uiImage! + } } diff --git a/Meshtastic/Views/Nodes/Helpers/Map/MapSettingsForm.swift b/Meshtastic/Views/Nodes/Helpers/Map/MapSettingsForm.swift index 304c5e69..ca17c6fa 100644 --- a/Meshtastic/Views/Nodes/Helpers/Map/MapSettingsForm.swift +++ b/Meshtastic/Views/Nodes/Helpers/Map/MapSettingsForm.swift @@ -84,15 +84,12 @@ struct MapSettingsForm: View { Label("Node History", systemImage: "building.columns.fill") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - .onTapGesture { - self.nodeHistory.toggle() - UserDefaults.enableMapNodeHistoryPins = self.nodeHistory + Toggle(isOn: $enableMapRouteLines) { + Label("Route Lines", systemImage: "road.lanes") } + .tint(.accentColor) + } - Toggle(isOn: $enableMapRouteLines) { - Label("Route Lines", systemImage: "road.lanes") - } - .tint(.accentColor) Toggle(isOn: $convexHull) { Label("Convex Hull", systemImage: "button.angledbottom.horizontal.right") } diff --git a/Meshtastic/Views/Nodes/Helpers/Map/NodeMapSwiftUI.swift b/Meshtastic/Views/Nodes/Helpers/Map/NodeMapSwiftUI.swift index 67707590..5181586c 100644 --- a/Meshtastic/Views/Nodes/Helpers/Map/NodeMapSwiftUI.swift +++ b/Meshtastic/Views/Nodes/Helpers/Map/NodeMapSwiftUI.swift @@ -9,6 +9,26 @@ import SwiftUI import CoreLocation import MapKit +struct NodeMapContentSignature: Equatable { + // Used to decide if NodeMapContent needs to be reevaluated. + // Only include fields that are used within NodeMapContent (or approximations like positionCount and lastPositionTime). + let nodeNum: Int64 + let positionCount: Int + let lastPositionTime: Date? + let showNodeHistory: Bool + let showRouteLines: Bool + let showConvexHull: Bool + let favorite: Bool +} + +private struct NodeMapContentEquatableWrapper: View, Equatable { + // Prevent slow, needless recomputation of NodeMapContent if the NodeMapContentSignature hasn't changed. + let signature: NodeMapContentSignature + @ViewBuilder let content: () -> Content + static func == (lhs: NodeMapContentEquatableWrapper, rhs: NodeMapContentEquatableWrapper) -> Bool { lhs.signature == rhs.signature } + var body: some View { content() } +} + struct NodeMapSwiftUI: View { @Environment(\.managedObjectContext) var context @EnvironmentObject var accessoryManager: AccessoryManager @@ -17,6 +37,9 @@ struct NodeMapSwiftUI: View { @State var showUserLocation: Bool = false @State var positions: [PositionEntity] = [] /// Map State User Defaults + @AppStorage("meshMapShowNodeHistory") private var showNodeHistory = false + @AppStorage("meshMapShowRouteLines") private var showRouteLines = false + @AppStorage("enableMapConvexHull") private var showConvexHull = false @AppStorage("enableMapTraffic") private var showTraffic: Bool = false @AppStorage("enableMapPointsOfInterest") private var showPointsOfInterest: Bool = false @AppStorage("mapLayer") private var selectedMapLayer: MapLayer = .hybrid @@ -91,9 +114,17 @@ struct NodeMapSwiftUI: View { } } + private var mapContentSignature: NodeMapContentSignature { + let positionCount = node.positions?.count ?? 0 + let lastPositionTime = (node.positions?.lastObject as? PositionEntity)?.time + return NodeMapContentSignature(nodeNum: node.num, positionCount: positionCount, lastPositionTime: lastPositionTime, showNodeHistory: showNodeHistory, showRouteLines: showRouteLines, showConvexHull: showConvexHull, favorite: node.favorite) + } + private var baseMap: some View { - Map(position: $position, bounds: MapCameraBounds(minimumDistance: 0, maximumDistance: .infinity), scope: mapScope) { - NodeMapContent(node: node) + NodeMapContentEquatableWrapper(signature: mapContentSignature) { + Map(position: $position, bounds: MapCameraBounds(minimumDistance: 0, maximumDistance: .infinity), scope: mapScope) { + NodeMapContent(node: node) + } } .mapScope(mapScope) .mapStyle(mapStyle) @@ -110,6 +141,7 @@ struct NodeMapSwiftUI: View { .mapControlVisibility(.visible) } .controlSize(.regular) + .transaction { $0.animation = nil } } private var lookAroundView: some View { diff --git a/Meshtastic/Views/Nodes/Helpers/Map/PositionPopover.swift b/Meshtastic/Views/Nodes/Helpers/Map/PositionPopover.swift index e9d11aa4..8ab7b29f 100644 --- a/Meshtastic/Views/Nodes/Helpers/Map/PositionPopover.swift +++ b/Meshtastic/Views/Nodes/Helpers/Map/PositionPopover.swift @@ -12,13 +12,16 @@ struct PositionPopover: View { @ObservedObject var locationsHandler = LocationsHandler.shared @Environment(\.managedObjectContext) var context - @EnvironmentObject var router: Router + @EnvironmentObject var appState: AppState private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom } @Environment(\.dismiss) private var dismiss @Environment(\.openURL) var openURL var position: PositionEntity var popover: Bool = true let distanceFormatter = MKDistanceFormatter() + + @State private var detentSelection: PresentationDetent = .fraction(0.65) + @State private var navigateToCompass = false var body: some View { // Node Color from node.num @@ -29,7 +32,7 @@ struct PositionPopover: View { ZStack { Button { if let nodeNum = position.nodePosition?.num { - router.navigateToNodeDetail(nodeNum: Int64(nodeNum)) + appState.router.navigateToNodeDetail(nodeNum: Int64(nodeNum)) dismiss() } } label: { @@ -42,6 +45,19 @@ struct PositionPopover: View { Divider() HStack(alignment: .center) { VStack(alignment: .leading) { + Button { + detentSelection = .large + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { + navigateToCompass = true + } + } label: { + HStack { + Image(systemName: "safari") + Text("Open Compass") + } + } + .padding(.bottom, 5) + /// Time Label { if idiom != .phone { @@ -131,6 +147,7 @@ struct PositionPopover: View { } .padding(.bottom, 5) } + /// Heading let degrees = Angle.degrees(Double(position.heading)) Label { @@ -234,10 +251,17 @@ struct PositionPopover: View { #endif } } + .presentationDetents([.fraction(0.65), .large], selection: $detentSelection) + .presentationContentInteraction(.scrolls) + .presentationDragIndicator(.visible) + .presentationBackgroundInteraction(.enabled(upThrough: .large)) + .navigationDestination(isPresented: $navigateToCompass) { + CompassView( + waypointLocation: position.coordinate, + waypointName: position.nodePosition?.user?.longName ?? "Unknown node", + color: (position.nodePosition?.user?.num != nil && position.nodePosition?.user?.num != 0) ? Color(UIColor(hex: UInt32(position.nodePosition!.user!.num))) : .orange + ) + } } - .presentationDetents([.fraction(0.65), .large]) - .presentationContentInteraction(.scrolls) - .presentationDragIndicator(.visible) - .presentationBackgroundInteraction(.enabled(upThrough: .large)) } } diff --git a/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift b/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift index 7ad12716..dc394f35 100644 --- a/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift @@ -27,6 +27,7 @@ struct NodeDetail: View { var connectedNode: NodeInfoEntity? @ObservedObject var node: NodeInfoEntity @State private var environmentSectionHeight: CGFloat = 0 + @State var showingCompassSheet = false var body: some View { NavigationStack { @@ -40,7 +41,7 @@ struct NodeDetail: View { context: context ) Section("Hardware") { - + NodeInfoItem(node: node) // .id("topOfList") } @@ -260,18 +261,18 @@ struct NodeDetail: View { // Update the state with the new height self.environmentSectionHeight = newHeight } - } else { + } else if let metrics = node.latestEnvironmentMetrics { // 👈 REFACTORED: Unwraps metrics safely VStack { - if node.latestEnvironmentMetrics?.iaq ?? -1 > 0 { - IndoorAirQuality(iaq: Int(node.latestEnvironmentMetrics?.iaq ?? 0), displayMode: .gradient) + if metrics.iaq ?? -1 > 0 { // Use unwrapped 'metrics' + IndoorAirQuality(iaq: Int(metrics.iaq ?? 0), displayMode: .gradient) .padding(.vertical) } LazyVGrid(columns: gridItemLayout) { - if let temperature = node.latestEnvironmentMetrics?.temperature?.shortFormattedTemperature() { + if let temperature = metrics.temperature?.shortFormattedTemperature() { WeatherConditionsCompactWidget(temperature: String(temperature), symbolName: "cloud.sun", description: "TEMP") } - if let humidity = node.latestEnvironmentMetrics?.relativeHumidity { - if let temperature = node.latestEnvironmentMetrics?.temperature { + if let humidity = metrics.relativeHumidity { + if let temperature = metrics.temperature { let dewPoint = calculateDewPoint(temp: temperature, relativeHumidity: humidity) .formatted(.number.precision(.fractionLength(0))) + "°" HumidityCompactWidget(humidity: Int(humidity), dewPoint: dewPoint) @@ -279,17 +280,17 @@ struct NodeDetail: View { HumidityCompactWidget(humidity: Int(humidity), dewPoint: nil) } } - if let pressure = node.latestEnvironmentMetrics?.barometricPressure { + if let pressure = metrics.barometricPressure { PressureCompactWidget(pressure: pressure.formatted(.number.precision(.fractionLength(2))), unit: "hPA", low: pressure <= 1009.144) } - if let windSpeed = node.latestEnvironmentMetrics?.windSpeed { + if let windSpeed = metrics.windSpeed { let windSpeedMeasurement = Measurement(value: Double(windSpeed), unit: UnitSpeed.metersPerSecond) - let windGust = node.latestEnvironmentMetrics?.windGust.map { Measurement(value: Double($0), unit: UnitSpeed.metersPerSecond) } - let direction = cardinalValue(from: Double(node.latestEnvironmentMetrics?.windDirection ?? 0)) + let windGust = metrics.windGust.map { Measurement(value: Double($0), unit: UnitSpeed.metersPerSecond) } + let direction = cardinalValue(from: Double(metrics.windDirection ?? 0)) // Use unwrapped 'metrics' WindCompactWidget(speed: windSpeedMeasurement.formatted(.measurement(width: .abbreviated, numberFormatStyle: .number.precision(.fractionLength(0)))), - gust: node.latestEnvironmentMetrics?.windGust ?? 0.0 > 0.0 ? windGust?.formatted(.measurement(width: .abbreviated, numberFormatStyle: .number.precision(.fractionLength(0)))) : "", direction: direction) + gust: metrics.windGust ?? 0.0 > 0.0 ? windGust?.formatted(.measurement(width: .abbreviated, numberFormatStyle: .number.precision(.fractionLength(0)))) : "", direction: direction) } - if let rainfall1h = node.latestEnvironmentMetrics?.rainfall1H { + if let rainfall1h = metrics.rainfall1H { let locale = NSLocale.current as NSLocale let usesMetricSystem = locale.usesMetricSystem // Returns true for metric (mm), false for imperial (inches) let unit = usesMetricSystem ? UnitLength.millimeters : UnitLength.inches @@ -299,7 +300,7 @@ struct NodeDetail: View { let formattedRain = measurement.converted(to: unit).value.formatted(.number.precision(.fractionLength(decimals))) RainfallCompactWidget(timespan: .rainfall1H, rainfall: formattedRain, unit: unitLabel) } - if let rainfall24h = node.latestEnvironmentMetrics?.rainfall24H { + if let rainfall24h = metrics.rainfall24H { let locale = NSLocale.current as NSLocale let usesMetricSystem = locale.usesMetricSystem // Returns true for metric (mm), false for imperial (inches) let unit = usesMetricSystem ? UnitLength.millimeters : UnitLength.inches @@ -309,26 +310,26 @@ struct NodeDetail: View { let formattedRain = measurement.converted(to: unit).value.formatted(.number.precision(.fractionLength(decimals))) RainfallCompactWidget(timespan: .rainfall24H, rainfall: formattedRain, unit: unitLabel) } - if let radiation = node.latestEnvironmentMetrics?.radiation { + if let radiation = metrics.radiation { RadiationCompactWidget(radiation: radiation.formatted(.number.precision(.fractionLength(1))), unit: "µR/hr") } - if let weight = node.latestEnvironmentMetrics?.weight { + if let weight = metrics.weight { WeightCompactWidget(weight: weight.formatted(.number.precision(.fractionLength(1))), unit: "kg") } - if let distance = node.latestEnvironmentMetrics?.distance { + if let distance = metrics.distance { DistanceCompactWidget(distance: distance.formatted(.number.precision(.fractionLength(0))), unit: "mm") } - if let soilTemperature = node.latestEnvironmentMetrics?.soilTemperature { + if let soilTemperature = metrics.soilTemperature { let locale = NSLocale.current as NSLocale let localeUnit = locale.object(forKey: NSLocale.Key(rawValue: "kCFLocaleTemperatureUnitKey")) let unit = localeUnit as? String ?? "Celsius" == "Fahrenheit" ? "°F" : "°C" SoilTemperatureCompactWidget(temperature: soilTemperature.localeTemperature().formatted(.number.precision(.fractionLength(0))), unit: unit) } - if let soilMoisture = node.latestEnvironmentMetrics?.soilMoisture { + if let soilMoisture = metrics.soilMoisture { SoilMoistureCompactWidget(moisture: soilMoisture.formatted(.number.precision(.fractionLength(0))), unit: "%") } } - .padding(node.latestEnvironmentMetrics?.iaq ?? -1 > 0 ? .bottom : .vertical) + .padding(metrics.iaq ?? -1 > 0 ? .bottom : .vertical) } } } @@ -460,7 +461,8 @@ struct NodeDetail: View { } } ExchangePositionsButton( - node: node + node: node, + connectedNode: connectedNode ) TraceRouteButton( node: node @@ -472,6 +474,17 @@ struct NodeDetail: View { ) } if node.hasPositions { + #if !targetEnvironment(macCatalyst) + Button { + showingCompassSheet = true + } label: { + Label { + Text("Open Compass") + } icon: { + Image(systemName: "safari") + } + } + #endif NavigateToButton(node: node) } IgnoreNodeButton( @@ -558,6 +571,9 @@ struct NodeDetail: View { } } } + .sheet(isPresented: $showingCompassSheet) { + CompassView(waypointLocation: node.latestPosition?.nodeCoordinate ?? nil, waypointName: node.user?.longName ?? nil, color: Color(UIColor(hex: UInt32(node.num)))) + } .onAppear { scrollView.scrollTo("topOfList", anchor: .top) } diff --git a/Meshtastic/Views/Nodes/Helpers/ShareContactQRDialog.swift b/Meshtastic/Views/Nodes/Helpers/ShareContactQRDialog.swift index 4bc56e24..cf30b441 100644 --- a/Meshtastic/Views/Nodes/Helpers/ShareContactQRDialog.swift +++ b/Meshtastic/Views/Nodes/Helpers/ShareContactQRDialog.swift @@ -13,12 +13,14 @@ import MeshtasticProtobufs import OSLog struct ShareContactQRDialog: View { + let manuallyVerified = false let node: NodeInfo @Environment(\.dismiss) private var dismiss var qrString: String { var contact = SharedContact() contact.nodeNum = node.num contact.user = node.user + contact.manuallyVerified = manuallyVerified do { let contactString = try contact.serializedData().base64EncodedString() return ("https://meshtastic.org/v/#" + contactString.base64ToBase64url()) diff --git a/Meshtastic/Views/Nodes/MeshMap.swift b/Meshtastic/Views/Nodes/MeshMap.swift index 9d8911ab..d72987da 100644 --- a/Meshtastic/Views/Nodes/MeshMap.swift +++ b/Meshtastic/Views/Nodes/MeshMap.swift @@ -75,7 +75,8 @@ struct MeshMap: View { } .controlSize(.regular) .offset(y: 100) - .onMapCameraChange(frequency: MapCameraUpdateFrequency.continuous, { context in + .onMapCameraChange(frequency: MapCameraUpdateFrequency.onEnd, { context in + // distance is only used for long-press waypoint creation, so we don't need continuous updates which touch @State and force rerenders as we pan and (for distance in particular) zoom around the map. onEnd is more than enough. distance = context.camera.distance }) .onTapGesture(count: 1, perform: { position in diff --git a/Meshtastic/Views/Nodes/NodeList.swift b/Meshtastic/Views/Nodes/NodeList.swift index 34393253..f324ed8b 100644 --- a/Meshtastic/Views/Nodes/NodeList.swift +++ b/Meshtastic/Views/Nodes/NodeList.swift @@ -24,14 +24,14 @@ struct NodeList: View { @StateObject var filters = NodeFilterParameters() @State var isEditingFilters = false @SceneStorage("selectedDetailView") var selectedDetailView: String? - + var connectedNode: NodeInfoEntity? { if let num = accessoryManager.activeDeviceNum { return getNodeInfo(id: num, context: context) } return nil } - + var body: some View { NavigationSplitView { FilteredNodeList( @@ -137,7 +137,7 @@ struct NodeList: View { } } } - + // Helper to get the count of nodes for the navigation title private func getNodeCount() -> Int { let request: NSFetchRequest = NodeInfoEntity.fetchRequest() @@ -154,7 +154,7 @@ fileprivate struct FilteredNodeList: View { @EnvironmentObject var accessoryManager: AccessoryManager @FetchRequest private var nodes: FetchedResults @Environment(\.managedObjectContext) var context - + @Binding var selectedNode: NodeInfoEntity? var connectedNode: NodeInfoEntity? @Binding var isPresentingDeleteNodeAlert: Bool @@ -179,17 +179,19 @@ fileprivate struct FilteredNodeList: View { ] request.predicate = withFilters.buildPredicate() self._nodes = FetchRequest(fetchRequest: request) - + self._selectedNode = selectedNode self.connectedNode = connectedNode self._isPresentingDeleteNodeAlert = isPresentingDeleteNodeAlert self._deleteNodeId = deleteNodeId self._shareContactNode = shareContactNode } - + // The body of the view var body: some View { - List(nodes, id: \.self, selection: $selectedNode) { node in + // If the connected node passes filters, always show it first + let nodesWithConnectedFirst = nodes.filter { $0.num == accessoryManager.activeDeviceNum } + nodes.filter { $0.num != accessoryManager.activeDeviceNum } + List(nodesWithConnectedFirst, id: \.self, selection: $selectedNode) { node in NavigationLink(value: node) { NodeListItem( node: node, @@ -205,7 +207,7 @@ fileprivate struct FilteredNodeList: View { } } } - + @ViewBuilder func contextMenuActions( node: NodeInfoEntity, @@ -276,7 +278,7 @@ fileprivate struct FilteredNodeList: View { fileprivate extension NodeFilterParameters { func buildPredicate() -> NSPredicate? { var predicates: [NSPredicate] = [] - + // Search text predicates if !searchText.isEmpty { let searchKeys = [ @@ -288,19 +290,19 @@ fileprivate extension NodeFilterParameters { } predicates.append(NSCompoundPredicate(orPredicateWithSubpredicates: textPredicates)) } - + // Favorite filter if isFavorite { predicates.append(NSPredicate(format: "favorite == YES")) } - + // Via Lora/MQTT filters if viaLora && !viaMqtt { predicates.append(NSPredicate(format: "viaMqtt == NO")) } else if !viaLora && viaMqtt { predicates.append(NSPredicate(format: "viaMqtt == YES")) } - + // Role filter if roleFilter && !deviceRoles.isEmpty { let rolesPredicates = deviceRoles.map { @@ -308,41 +310,41 @@ fileprivate extension NodeFilterParameters { } predicates.append(NSCompoundPredicate(type: .or, subpredicates: rolesPredicates)) } - + // Hops Away filter if hopsAway == 0.0 { predicates.append(NSPredicate(format: "hopsAway == %i", 0)) } else if hopsAway > 0.0 { predicates.append(NSPredicate(format: "hopsAway > 0 AND hopsAway <= %i", Int32(hopsAway))) } - + // Online filter if isOnline { let isOnlinePredicate = NSPredicate(format: "lastHeard >= %@", Calendar.current.date(byAdding: .minute, value: -120, to: Date())! as NSDate) predicates.append(isOnlinePredicate) } - + // Encrypted filter if isPkiEncrypted { predicates.append(NSPredicate(format: "user.pkiEncrypted == YES")) } - + // Ignored filter if isIgnored { predicates.append(NSPredicate(format: "ignored == YES")) } else { predicates.append(NSPredicate(format: "ignored == NO")) } - + // Environment filter if isEnvironment { predicates.append(NSPredicate(format: "SUBQUERY(telemetries, $tel, $tel.metricsType == 1).@count > 0")) } - + // Distance filter if distanceFilter { if let pointOfInterest = LocationsHandler.currentLocation { - + if pointOfInterest.latitude != LocationsHandler.DefaultLocation.latitude && pointOfInterest.longitude != LocationsHandler.DefaultLocation.longitude { let d: Double = maxDistance * 1.1 let r: Double = 6371009 diff --git a/Meshtastic/Views/Onboarding/DeviceOnboarding.swift b/Meshtastic/Views/Onboarding/DeviceOnboarding.swift index 6f7c87a3..d95d6977 100644 --- a/Meshtastic/Views/Onboarding/DeviceOnboarding.swift +++ b/Meshtastic/Views/Onboarding/DeviceOnboarding.swift @@ -59,7 +59,7 @@ struct DeviceOnboarding: View { makeRow( icon: "person.2.shield", title: String(localized: "User Privacy"), - subtitle: String(localized: "Meshtastic does not collect any personal information. We do anonymously collect usage and crash data to improve the app. This helps us understand how the app is being used and where we can make improvements. The data we collect is non-personally identifiable and cannot be linked to you as an individual. You can opt out of this under app settings.") + subtitle: String(localized: "Meshtastic does not collect any personal information. We do anonymously collect usage and crash data to improve the app. You can opt out under app settings.") ) } .padding() diff --git a/Meshtastic/Views/Settings/AppSettings.swift b/Meshtastic/Views/Settings/AppSettings.swift index 4e1ab892..2f10c2af 100644 --- a/Meshtastic/Views/Settings/AppSettings.swift +++ b/Meshtastic/Views/Settings/AppSettings.swift @@ -56,6 +56,9 @@ struct AppSettings: View { } .tint(.accentColor) } +#if targetEnvironment(macCatalyst) + // App Icon Picker is disabled on macOS Catalyst +#else Button { isPresentingAppIconSheet.toggle() } label: { @@ -65,6 +68,7 @@ struct AppSettings: View { AppIconPicker(isPresenting: self.$isPresentingAppIconSheet) .presentationDetents([.medium]) } +#endif } Section(header: Text("environment")) { VStack(alignment: .leading) { diff --git a/Meshtastic/Views/Settings/Channels.swift b/Meshtastic/Views/Settings/Channels.swift index 29f76966..f5c89071 100644 --- a/Meshtastic/Views/Settings/Channels.swift +++ b/Meshtastic/Views/Settings/Channels.swift @@ -217,13 +217,13 @@ struct Channels: View { } Task { _ = try await accessoryManager.saveChannel(channel: channel, fromUser: node!.user!, toUser: node!.user!) - Task { @MainActor in selectedChannel = nil channelName = "" channelRole = 2 hasChanges = false } + accessoryManager.mqttManager.connectFromConfigSettings(node: node!) } } label: { Label("Save", systemImage: "square.and.arrow.down") diff --git a/Meshtastic/Views/Settings/Config/DeviceConfig.swift b/Meshtastic/Views/Settings/Config/DeviceConfig.swift index 80fe3dba..a03c8a22 100644 --- a/Meshtastic/Views/Settings/Config/DeviceConfig.swift +++ b/Meshtastic/Views/Settings/Config/DeviceConfig.swift @@ -29,8 +29,9 @@ struct DeviceConfig: View { @State var ledHeartbeatEnabled = true @State var tripleClickAsAdHocPing = true @State var tzdef = "" - @State private var showRouterWarning = false - + @State private var showSpecialRoleWarning = false + @State private var showSpecialRoleWarningForRole: Int = 0 + var body: some View { Form { ConfigHeader(title: "Device", config: \.deviceConfig, node: node, onAppear: setDeviceValues) @@ -43,13 +44,14 @@ struct DeviceConfig: View { } } .onChange(of: deviceRole) { _, newRole in - if hasChanges && [DeviceRoles.router.rawValue, DeviceRoles.routerLate.rawValue].contains(newRole) { - showRouterWarning = true + if hasChanges && [DeviceRoles.router.rawValue, DeviceRoles.routerLate.rawValue, DeviceRoles.clientBase.rawValue].contains(newRole) { + showSpecialRoleWarningForRole = newRole + showSpecialRoleWarning = true } } .confirmationDialog( "Are you sure?", - isPresented: $showRouterWarning, + isPresented: $showSpecialRoleWarning, titleVisibility: .visible ) { @@ -60,7 +62,7 @@ struct DeviceConfig: View { setDeviceValues() } } message: { - Text("The Router roles are only for high vantage locations like mountaintops and towers with few nearby nodes, not for use in urban areas. Improper use will hurt your local mesh.") + Text(specialRoleWarningMessage(newRole: showSpecialRoleWarningForRole)) } Text(DeviceRoles(rawValue: deviceRole)?.description ?? "") .foregroundColor(.gray) @@ -332,4 +334,14 @@ struct DeviceConfig: View { self.tzdef = node?.deviceConfig?.tzdef ?? "" hasChanges = false } + + private func specialRoleWarningMessage(newRole: Int) -> String { + if [DeviceRoles.router.rawValue, DeviceRoles.routerLate.rawValue].contains(newRole) { + return "The Router roles are only for high vantage locations like mountaintops and towers with few nearby nodes, not for use in urban areas. Improper use will hurt your local mesh." + } else if newRole == DeviceRoles.clientBase.rawValue { + return "Switching to Client Base will clear this node's favorites. Client Base should only favorite other nodes you control. Improper use will hurt your local mesh." + } else { + return "" + } + } } diff --git a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift index 985bd878..5336c5ef 100644 --- a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift @@ -263,6 +263,7 @@ struct MQTTConfig: View { mqtt.jsonEnabled = self.jsonEnabled mqtt.tlsEnabled = self.tlsEnabled mqtt.mapReportingEnabled = self.mapReportingEnabled + mqtt.mapReportSettings.shouldReportLocation = UserDefaults.mapReportingOptIn mqtt.mapReportSettings.positionPrecision = UInt32(self.mapPositionPrecision) mqtt.mapReportSettings.publishIntervalSecs = UInt32(self.mapPublishIntervalSecs.intValue) Task { diff --git a/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift b/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift index 83958ff7..ed65ce4e 100644 --- a/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift @@ -5,6 +5,7 @@ // Copyright (c) Garth Vander Houwen 6/13/22. // import MeshtasticProtobufs +import CoreData import OSLog import SwiftUI @@ -21,6 +22,19 @@ struct RangeTestConfig: View { @State var enabled = false @State var save = false @State private var sender: UpdateInterval = UpdateInterval(from: 0) + private var isPrimaryChannelPublic: Bool { + guard let channels = node?.myInfo?.channels?.array as? [ChannelEntity] else { + return false + } + // Treat the primary channel on this node as "public" when it is effectively unencrypted + // or using a minimal 1-byte key (hexDescription shorter than 3 characters). + guard let primary = channels.first(where: { $0.index == 0 && $0.role > 0 }) else { + return false + } + let hexLen = primary.psk?.hexDescription.count ?? 0 + return hexLen < 3 + } + var body: some View { Form { @@ -51,14 +65,15 @@ struct RangeTestConfig: View { } } - .disabled(!accessoryManager.isConnected || node?.rangeTestConfig == nil) + .disabled(!accessoryManager.isConnected || node?.rangeTestConfig == nil || isPrimaryChannelPublic) .safeAreaInset(edge: .bottom, alignment: .center) { HStack(spacing: 0) { SaveConfigButton(node: node, hasChanges: $hasChanges) { let connectedNode = getNodeInfo(id: accessoryManager.activeDeviceNum ?? -1, context: context) if connectedNode != nil { var rtc = ModuleConfig.RangeTestConfig() - rtc.enabled = enabled + let effectiveEnabled = isPrimaryChannelPublic ? false : enabled + rtc.enabled = effectiveEnabled rtc.save = save rtc.sender = UInt32(sender.intValue) Task { @@ -110,6 +125,8 @@ struct RangeTestConfig: View { } .onChange(of: enabled) { _, newEnabled in if newEnabled != node?.rangeTestConfig?.enabled { hasChanges = true } + + // Note: even if this is the connected node, we don't have to update AccessoryManager.wantRangeTestPackets here, because the node will reboot after we save config changes, and we'll pick up the new value after we reconnect. } .onChange(of: save) { _, newSave in if newSave != node?.rangeTestConfig?.save { hasChanges = true } diff --git a/Meshtastic/Views/Settings/Config/Module/StoreForwardConfig.swift b/Meshtastic/Views/Settings/Config/Module/StoreForwardConfig.swift index 397945fb..bc35258d 100644 --- a/Meshtastic/Views/Settings/Config/Module/StoreForwardConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/StoreForwardConfig.swift @@ -167,6 +167,8 @@ struct StoreForwardConfig: View { } .onChange(of: enabled) { oldEnabled, newEnabled in if oldEnabled != newEnabled && newEnabled != node!.storeForwardConfig!.enabled { hasChanges = true } + + // Note: even if this is the connected node, we don't have to update AccessoryManager.wantStoreAndForwardPackets here, because the node will reboot after we save config changes, and we'll pick up the new value after we reconnect. } .onChange(of: isServer) { oldIsServer, newIsServer in if oldIsServer != newIsServer && newIsServer != node!.storeForwardConfig!.isRouter { hasChanges = true } diff --git a/Meshtastic/Views/Settings/SaveChannelQRCode.swift b/Meshtastic/Views/Settings/SaveChannelQRCode.swift index 71a032b4..089b12c2 100644 --- a/Meshtastic/Views/Settings/SaveChannelQRCode.swift +++ b/Meshtastic/Views/Settings/SaveChannelQRCode.swift @@ -9,11 +9,17 @@ import CoreData import OSLog import MeshtasticProtobufs +struct SaveChannelLinkData: Identifiable { + let id = UUID() + let data: String + let add: Bool +} + struct SaveChannelQRCode: View { @Environment(\.dismiss) private var dismiss @Environment(\.managedObjectContext) var context let channelSetLink: String - var addChannels: Bool = false + @State var addChannels: Bool = false var accessoryManager: AccessoryManager @State private var showError: Bool = false @@ -25,13 +31,13 @@ struct SaveChannelQRCode: View { VStack { Text("\(addChannels ? "Add" : "Replace all") Channels?") .font(.title) - Text("These settings will \(addChannels ? "add" : "replace all") channels. The current LoRa Config will be replaced, if there are substantial changes to the LoRa config the device will reboot") + Text("These settings will \(addChannels ? "add channels without changing any LoRa config values." : "replace all channels. The current LoRa Config will be replaced, if there are substantial changes to the LoRa config the device will reboot automatically.")") .fixedSize(horizontal: false, vertical: true) .foregroundColor(.gray) .font(.title3) .padding() - if !loraChanges.isEmpty { + if !loraChanges.isEmpty && !addChannels { VStack(alignment: .leading) { Text("LoRa Config Changes:") .font(.headline) diff --git a/Meshtastic/Views/Settings/UpdateIntervalPicker.swift b/Meshtastic/Views/Settings/UpdateIntervalPicker.swift index b7d6d153..c8601624 100644 --- a/Meshtastic/Views/Settings/UpdateIntervalPicker.swift +++ b/Meshtastic/Views/Settings/UpdateIntervalPicker.swift @@ -1,15 +1,15 @@ // -// UpdateIntervalPicker.swift -// Meshtastic +//  UpdateIntervalPicker.swift +//  Meshtastic // -// Copyright(c) Garth Vander Houwen 10/4/25. +//  Copyright(c) Garth Vander Houwen 10/4/25. // import SwiftUI struct UpdateIntervalPicker: View { let config: IntervalConfiguration let pickerLabel: String - let formatter = DateComponentsFormatter() + let formatter: DateComponentsFormatter // Make it a stored property @Binding var selectedInterval: UpdateInterval @@ -17,11 +17,14 @@ struct UpdateIntervalPicker: View { config.allowedCases .map { UpdateInterval(from: $0.rawValue) } } - + init(config: IntervalConfiguration, pickerLabel: String, selectedInterval: Binding) { self.config = config self.pickerLabel = pickerLabel self._selectedInterval = selectedInterval + let f = DateComponentsFormatter() + f.unitsStyle = .full + self.formatter = f } var body: some View { @@ -36,7 +39,7 @@ struct UpdateIntervalPicker: View { if isOutOfRange { let interval: TimeInterval = Double(selectedInterval.intValue) if let formattedString = formatter.string(from: interval) { - Text("⚠️ The configured value: (\(formattedString) seconds) is not one of the optimized options.") + Text("⚠️ The configured value: (\(formattedString)) is not one of the optimized options.") .font(.caption) .foregroundColor(.orange) } @@ -44,11 +47,11 @@ struct UpdateIntervalPicker: View { } } private var isOutOfRange: Bool { - switch selectedInterval.type { - case .manual: - return true - case .fixed(let fixedCase): - return !config.allowedCases.contains(fixedCase) - } - } + switch selectedInterval.type { + case .manual: + return true + case .fixed(let fixedCase): + return !config.allowedCases.contains(fixedCase) + } + } } diff --git a/MeshtasticProtobufs/Package.swift b/MeshtasticProtobufs/Package.swift index d329c439..f8715190 100644 --- a/MeshtasticProtobufs/Package.swift +++ b/MeshtasticProtobufs/Package.swift @@ -11,7 +11,7 @@ let package = Package( ), ], dependencies: [ - .package(url: "https://github.com/apple/swift-protobuf.git", from: "1.19.0"), + .package(url: "https://github.com/apple/swift-protobuf.git", from: "1.33.3"), ], targets: [ .target( diff --git a/MeshtasticProtobufs/Sources/meshtastic/admin.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/admin.pb.swift index 138d2ed1..fa6a9e61 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/admin.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/admin.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/admin.proto @@ -24,7 +25,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// This message is handled by the Admin module and is responsible for all settings/channel read/write operations. /// This message is used to do settings operations to both remote AND local nodes. /// (Prior to 1.2 these operations were done via special ToRadio operations) -public struct AdminMessage { +public struct AdminMessage: Sendable { // 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. @@ -582,10 +583,11 @@ public struct AdminMessage { /// /// Tell the node to reset the nodedb. - public var nodedbReset: Int32 { + /// When true, favorites are preserved through reset. + public var nodedbReset: Bool { get { if case .nodedbReset(let v)? = payloadVariant {return v} - return 0 + return false } set {payloadVariant = .nodedbReset(newValue)} } @@ -594,7 +596,7 @@ public struct AdminMessage { /// /// TODO: REPLACE - public enum OneOf_PayloadVariant: Equatable { + public enum OneOf_PayloadVariant: Equatable, Sendable { /// /// Send the specified channel in the response to this message /// NOTE: This field is sent with the channel index + 1 (to ensure we never try to send 'zero' - which protobufs treats as not present) @@ -767,239 +769,14 @@ public struct AdminMessage { case factoryResetConfig(Int32) /// /// Tell the node to reset the nodedb. - case nodedbReset(Int32) + /// When true, favorites are preserved through reset. + case nodedbReset(Bool) - #if !swift(>=4.1) - public static func ==(lhs: AdminMessage.OneOf_PayloadVariant, rhs: AdminMessage.OneOf_PayloadVariant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.getChannelRequest, .getChannelRequest): return { - guard case .getChannelRequest(let l) = lhs, case .getChannelRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getChannelResponse, .getChannelResponse): return { - guard case .getChannelResponse(let l) = lhs, case .getChannelResponse(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getOwnerRequest, .getOwnerRequest): return { - guard case .getOwnerRequest(let l) = lhs, case .getOwnerRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getOwnerResponse, .getOwnerResponse): return { - guard case .getOwnerResponse(let l) = lhs, case .getOwnerResponse(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getConfigRequest, .getConfigRequest): return { - guard case .getConfigRequest(let l) = lhs, case .getConfigRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getConfigResponse, .getConfigResponse): return { - guard case .getConfigResponse(let l) = lhs, case .getConfigResponse(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getModuleConfigRequest, .getModuleConfigRequest): return { - guard case .getModuleConfigRequest(let l) = lhs, case .getModuleConfigRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getModuleConfigResponse, .getModuleConfigResponse): return { - guard case .getModuleConfigResponse(let l) = lhs, case .getModuleConfigResponse(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getCannedMessageModuleMessagesRequest, .getCannedMessageModuleMessagesRequest): return { - guard case .getCannedMessageModuleMessagesRequest(let l) = lhs, case .getCannedMessageModuleMessagesRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getCannedMessageModuleMessagesResponse, .getCannedMessageModuleMessagesResponse): return { - guard case .getCannedMessageModuleMessagesResponse(let l) = lhs, case .getCannedMessageModuleMessagesResponse(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getDeviceMetadataRequest, .getDeviceMetadataRequest): return { - guard case .getDeviceMetadataRequest(let l) = lhs, case .getDeviceMetadataRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getDeviceMetadataResponse, .getDeviceMetadataResponse): return { - guard case .getDeviceMetadataResponse(let l) = lhs, case .getDeviceMetadataResponse(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getRingtoneRequest, .getRingtoneRequest): return { - guard case .getRingtoneRequest(let l) = lhs, case .getRingtoneRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getRingtoneResponse, .getRingtoneResponse): return { - guard case .getRingtoneResponse(let l) = lhs, case .getRingtoneResponse(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getDeviceConnectionStatusRequest, .getDeviceConnectionStatusRequest): return { - guard case .getDeviceConnectionStatusRequest(let l) = lhs, case .getDeviceConnectionStatusRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getDeviceConnectionStatusResponse, .getDeviceConnectionStatusResponse): return { - guard case .getDeviceConnectionStatusResponse(let l) = lhs, case .getDeviceConnectionStatusResponse(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setHamMode, .setHamMode): return { - guard case .setHamMode(let l) = lhs, case .setHamMode(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getNodeRemoteHardwarePinsRequest, .getNodeRemoteHardwarePinsRequest): return { - guard case .getNodeRemoteHardwarePinsRequest(let l) = lhs, case .getNodeRemoteHardwarePinsRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getNodeRemoteHardwarePinsResponse, .getNodeRemoteHardwarePinsResponse): return { - guard case .getNodeRemoteHardwarePinsResponse(let l) = lhs, case .getNodeRemoteHardwarePinsResponse(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.enterDfuModeRequest, .enterDfuModeRequest): return { - guard case .enterDfuModeRequest(let l) = lhs, case .enterDfuModeRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.deleteFileRequest, .deleteFileRequest): return { - guard case .deleteFileRequest(let l) = lhs, case .deleteFileRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setScale, .setScale): return { - guard case .setScale(let l) = lhs, case .setScale(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.backupPreferences, .backupPreferences): return { - guard case .backupPreferences(let l) = lhs, case .backupPreferences(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.restorePreferences, .restorePreferences): return { - guard case .restorePreferences(let l) = lhs, case .restorePreferences(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.removeBackupPreferences, .removeBackupPreferences): return { - guard case .removeBackupPreferences(let l) = lhs, case .removeBackupPreferences(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.sendInputEvent, .sendInputEvent): return { - guard case .sendInputEvent(let l) = lhs, case .sendInputEvent(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setOwner, .setOwner): return { - guard case .setOwner(let l) = lhs, case .setOwner(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setChannel, .setChannel): return { - guard case .setChannel(let l) = lhs, case .setChannel(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setConfig, .setConfig): return { - guard case .setConfig(let l) = lhs, case .setConfig(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setModuleConfig, .setModuleConfig): return { - guard case .setModuleConfig(let l) = lhs, case .setModuleConfig(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setCannedMessageModuleMessages, .setCannedMessageModuleMessages): return { - guard case .setCannedMessageModuleMessages(let l) = lhs, case .setCannedMessageModuleMessages(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setRingtoneMessage, .setRingtoneMessage): return { - guard case .setRingtoneMessage(let l) = lhs, case .setRingtoneMessage(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.removeByNodenum, .removeByNodenum): return { - guard case .removeByNodenum(let l) = lhs, case .removeByNodenum(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setFavoriteNode, .setFavoriteNode): return { - guard case .setFavoriteNode(let l) = lhs, case .setFavoriteNode(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.removeFavoriteNode, .removeFavoriteNode): return { - guard case .removeFavoriteNode(let l) = lhs, case .removeFavoriteNode(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setFixedPosition, .setFixedPosition): return { - guard case .setFixedPosition(let l) = lhs, case .setFixedPosition(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.removeFixedPosition, .removeFixedPosition): return { - guard case .removeFixedPosition(let l) = lhs, case .removeFixedPosition(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setTimeOnly, .setTimeOnly): return { - guard case .setTimeOnly(let l) = lhs, case .setTimeOnly(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getUiConfigRequest, .getUiConfigRequest): return { - guard case .getUiConfigRequest(let l) = lhs, case .getUiConfigRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getUiConfigResponse, .getUiConfigResponse): return { - guard case .getUiConfigResponse(let l) = lhs, case .getUiConfigResponse(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.storeUiConfig, .storeUiConfig): return { - guard case .storeUiConfig(let l) = lhs, case .storeUiConfig(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setIgnoredNode, .setIgnoredNode): return { - guard case .setIgnoredNode(let l) = lhs, case .setIgnoredNode(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.removeIgnoredNode, .removeIgnoredNode): return { - guard case .removeIgnoredNode(let l) = lhs, case .removeIgnoredNode(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.beginEditSettings, .beginEditSettings): return { - guard case .beginEditSettings(let l) = lhs, case .beginEditSettings(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.commitEditSettings, .commitEditSettings): return { - guard case .commitEditSettings(let l) = lhs, case .commitEditSettings(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.addContact, .addContact): return { - guard case .addContact(let l) = lhs, case .addContact(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.keyVerification, .keyVerification): return { - guard case .keyVerification(let l) = lhs, case .keyVerification(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.factoryResetDevice, .factoryResetDevice): return { - guard case .factoryResetDevice(let l) = lhs, case .factoryResetDevice(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.rebootOtaSeconds, .rebootOtaSeconds): return { - guard case .rebootOtaSeconds(let l) = lhs, case .rebootOtaSeconds(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.exitSimulator, .exitSimulator): return { - guard case .exitSimulator(let l) = lhs, case .exitSimulator(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.rebootSeconds, .rebootSeconds): return { - guard case .rebootSeconds(let l) = lhs, case .rebootSeconds(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.shutdownSeconds, .shutdownSeconds): return { - guard case .shutdownSeconds(let l) = lhs, case .shutdownSeconds(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.factoryResetConfig, .factoryResetConfig): return { - guard case .factoryResetConfig(let l) = lhs, case .factoryResetConfig(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.nodedbReset, .nodedbReset): return { - guard case .nodedbReset(let l) = lhs, case .nodedbReset(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } /// /// TODO: REPLACE - public enum ConfigType: SwiftProtobuf.Enum { + public enum ConfigType: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1079,11 +856,25 @@ public struct AdminMessage { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [AdminMessage.ConfigType] = [ + .deviceConfig, + .positionConfig, + .powerConfig, + .networkConfig, + .displayConfig, + .loraConfig, + .bluetoothConfig, + .securityConfig, + .sessionkeyConfig, + .deviceuiConfig, + ] + } /// /// TODO: REPLACE - public enum ModuleConfigType: SwiftProtobuf.Enum { + public enum ModuleConfigType: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1181,9 +972,26 @@ public struct AdminMessage { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [AdminMessage.ModuleConfigType] = [ + .mqttConfig, + .serialConfig, + .extnotifConfig, + .storeforwardConfig, + .rangetestConfig, + .telemetryConfig, + .cannedmsgConfig, + .audioConfig, + .remotehardwareConfig, + .neighborinfoConfig, + .ambientlightingConfig, + .detectionsensorConfig, + .paxcounterConfig, + ] + } - public enum BackupLocation: SwiftProtobuf.Enum { + public enum BackupLocation: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1215,11 +1023,17 @@ public struct AdminMessage { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [AdminMessage.BackupLocation] = [ + .flash, + .sd, + ] + } /// /// Input event message to be sent to the node. - public struct InputEvent { + public struct InputEvent: Sendable { // 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. @@ -1248,56 +1062,9 @@ public struct AdminMessage { public init() {} } -#if swift(>=4.2) - -extension AdminMessage.ConfigType: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [AdminMessage.ConfigType] = [ - .deviceConfig, - .positionConfig, - .powerConfig, - .networkConfig, - .displayConfig, - .loraConfig, - .bluetoothConfig, - .securityConfig, - .sessionkeyConfig, - .deviceuiConfig, - ] -} - -extension AdminMessage.ModuleConfigType: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [AdminMessage.ModuleConfigType] = [ - .mqttConfig, - .serialConfig, - .extnotifConfig, - .storeforwardConfig, - .rangetestConfig, - .telemetryConfig, - .cannedmsgConfig, - .audioConfig, - .remotehardwareConfig, - .neighborinfoConfig, - .ambientlightingConfig, - .detectionsensorConfig, - .paxcounterConfig, - ] -} - -extension AdminMessage.BackupLocation: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [AdminMessage.BackupLocation] = [ - .flash, - .sd, - ] -} - -#endif // swift(>=4.2) - /// /// Parameters for setting up Meshtastic for ameteur radio usage -public struct HamParameters { +public struct HamParameters: Sendable { // 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. @@ -1327,7 +1094,7 @@ public struct HamParameters { /// /// Response envelope for node_remote_hardware_pins -public struct NodeRemoteHardwarePinsResponse { +public struct NodeRemoteHardwarePinsResponse: Sendable { // 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. @@ -1341,7 +1108,7 @@ public struct NodeRemoteHardwarePinsResponse { public init() {} } -public struct SharedContact { +public struct SharedContact: Sendable { // 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. @@ -1378,7 +1145,7 @@ public struct SharedContact { /// /// This message is used by a client to initiate or complete a key verification -public struct KeyVerificationAdmin { +public struct KeyVerificationAdmin: Sendable { // 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. @@ -1408,7 +1175,7 @@ public struct KeyVerificationAdmin { /// /// Three stages of this request. - public enum MessageType: SwiftProtobuf.Enum { + public enum MessageType: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1453,6 +1220,14 @@ public struct KeyVerificationAdmin { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [KeyVerificationAdmin.MessageType] = [ + .initiateVerification, + .provideSecurityNumber, + .doVerify, + .doNotVerify, + ] + } public init() {} @@ -1460,97 +1235,13 @@ public struct KeyVerificationAdmin { fileprivate var _securityNumber: UInt32? = nil } -#if swift(>=4.2) - -extension KeyVerificationAdmin.MessageType: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [KeyVerificationAdmin.MessageType] = [ - .initiateVerification, - .provideSecurityNumber, - .doVerify, - .doNotVerify, - ] -} - -#endif // swift(>=4.2) - -#if swift(>=5.5) && canImport(_Concurrency) -extension AdminMessage: @unchecked Sendable {} -extension AdminMessage.OneOf_PayloadVariant: @unchecked Sendable {} -extension AdminMessage.ConfigType: @unchecked Sendable {} -extension AdminMessage.ModuleConfigType: @unchecked Sendable {} -extension AdminMessage.BackupLocation: @unchecked Sendable {} -extension AdminMessage.InputEvent: @unchecked Sendable {} -extension HamParameters: @unchecked Sendable {} -extension NodeRemoteHardwarePinsResponse: @unchecked Sendable {} -extension SharedContact: @unchecked Sendable {} -extension KeyVerificationAdmin: @unchecked Sendable {} -extension KeyVerificationAdmin.MessageType: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".AdminMessage" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 101: .standard(proto: "session_passkey"), - 1: .standard(proto: "get_channel_request"), - 2: .standard(proto: "get_channel_response"), - 3: .standard(proto: "get_owner_request"), - 4: .standard(proto: "get_owner_response"), - 5: .standard(proto: "get_config_request"), - 6: .standard(proto: "get_config_response"), - 7: .standard(proto: "get_module_config_request"), - 8: .standard(proto: "get_module_config_response"), - 10: .standard(proto: "get_canned_message_module_messages_request"), - 11: .standard(proto: "get_canned_message_module_messages_response"), - 12: .standard(proto: "get_device_metadata_request"), - 13: .standard(proto: "get_device_metadata_response"), - 14: .standard(proto: "get_ringtone_request"), - 15: .standard(proto: "get_ringtone_response"), - 16: .standard(proto: "get_device_connection_status_request"), - 17: .standard(proto: "get_device_connection_status_response"), - 18: .standard(proto: "set_ham_mode"), - 19: .standard(proto: "get_node_remote_hardware_pins_request"), - 20: .standard(proto: "get_node_remote_hardware_pins_response"), - 21: .standard(proto: "enter_dfu_mode_request"), - 22: .standard(proto: "delete_file_request"), - 23: .standard(proto: "set_scale"), - 24: .standard(proto: "backup_preferences"), - 25: .standard(proto: "restore_preferences"), - 26: .standard(proto: "remove_backup_preferences"), - 27: .standard(proto: "send_input_event"), - 32: .standard(proto: "set_owner"), - 33: .standard(proto: "set_channel"), - 34: .standard(proto: "set_config"), - 35: .standard(proto: "set_module_config"), - 36: .standard(proto: "set_canned_message_module_messages"), - 37: .standard(proto: "set_ringtone_message"), - 38: .standard(proto: "remove_by_nodenum"), - 39: .standard(proto: "set_favorite_node"), - 40: .standard(proto: "remove_favorite_node"), - 41: .standard(proto: "set_fixed_position"), - 42: .standard(proto: "remove_fixed_position"), - 43: .standard(proto: "set_time_only"), - 44: .standard(proto: "get_ui_config_request"), - 45: .standard(proto: "get_ui_config_response"), - 46: .standard(proto: "store_ui_config"), - 47: .standard(proto: "set_ignored_node"), - 48: .standard(proto: "remove_ignored_node"), - 64: .standard(proto: "begin_edit_settings"), - 65: .standard(proto: "commit_edit_settings"), - 66: .standard(proto: "add_contact"), - 67: .standard(proto: "key_verification"), - 94: .standard(proto: "factory_reset_device"), - 95: .standard(proto: "reboot_ota_seconds"), - 96: .standard(proto: "exit_simulator"), - 97: .standard(proto: "reboot_seconds"), - 98: .standard(proto: "shutdown_seconds"), - 99: .standard(proto: "factory_reset_config"), - 100: .standard(proto: "nodedb_reset"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}get_channel_request\0\u{3}get_channel_response\0\u{3}get_owner_request\0\u{3}get_owner_response\0\u{3}get_config_request\0\u{3}get_config_response\0\u{3}get_module_config_request\0\u{3}get_module_config_response\0\u{4}\u{2}get_canned_message_module_messages_request\0\u{3}get_canned_message_module_messages_response\0\u{3}get_device_metadata_request\0\u{3}get_device_metadata_response\0\u{3}get_ringtone_request\0\u{3}get_ringtone_response\0\u{3}get_device_connection_status_request\0\u{3}get_device_connection_status_response\0\u{3}set_ham_mode\0\u{3}get_node_remote_hardware_pins_request\0\u{3}get_node_remote_hardware_pins_response\0\u{3}enter_dfu_mode_request\0\u{3}delete_file_request\0\u{3}set_scale\0\u{3}backup_preferences\0\u{3}restore_preferences\0\u{3}remove_backup_preferences\0\u{3}send_input_event\0\u{4}\u{5}set_owner\0\u{3}set_channel\0\u{3}set_config\0\u{3}set_module_config\0\u{3}set_canned_message_module_messages\0\u{3}set_ringtone_message\0\u{3}remove_by_nodenum\0\u{3}set_favorite_node\0\u{3}remove_favorite_node\0\u{3}set_fixed_position\0\u{3}remove_fixed_position\0\u{3}set_time_only\0\u{3}get_ui_config_request\0\u{3}get_ui_config_response\0\u{3}store_ui_config\0\u{3}set_ignored_node\0\u{3}remove_ignored_node\0\u{4}\u{10}begin_edit_settings\0\u{3}commit_edit_settings\0\u{3}add_contact\0\u{3}key_verification\0\u{4}\u{1b}factory_reset_device\0\u{3}reboot_ota_seconds\0\u{3}exit_simulator\0\u{3}reboot_seconds\0\u{3}shutdown_seconds\0\u{3}factory_reset_config\0\u{3}nodedb_reset\0\u{3}session_passkey\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2073,8 +1764,8 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat } }() case 100: try { - var v: Int32? - try decoder.decodeSingularInt32Field(value: &v) + var v: Bool? + try decoder.decodeSingularBoolField(value: &v) if let v = v { if self.payloadVariant != nil {try decoder.handleConflictingOneOf()} self.payloadVariant = .nodedbReset(v) @@ -2306,7 +1997,7 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat }() case .nodedbReset?: try { guard case .nodedbReset(let v)? = self.payloadVariant else { preconditionFailure() } - try visitor.visitSingularInt32Field(value: v, fieldNumber: 100) + try visitor.visitSingularBoolField(value: v, fieldNumber: 100) }() case nil: break } @@ -2325,53 +2016,20 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat } extension AdminMessage.ConfigType: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "DEVICE_CONFIG"), - 1: .same(proto: "POSITION_CONFIG"), - 2: .same(proto: "POWER_CONFIG"), - 3: .same(proto: "NETWORK_CONFIG"), - 4: .same(proto: "DISPLAY_CONFIG"), - 5: .same(proto: "LORA_CONFIG"), - 6: .same(proto: "BLUETOOTH_CONFIG"), - 7: .same(proto: "SECURITY_CONFIG"), - 8: .same(proto: "SESSIONKEY_CONFIG"), - 9: .same(proto: "DEVICEUI_CONFIG"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0DEVICE_CONFIG\0\u{1}POSITION_CONFIG\0\u{1}POWER_CONFIG\0\u{1}NETWORK_CONFIG\0\u{1}DISPLAY_CONFIG\0\u{1}LORA_CONFIG\0\u{1}BLUETOOTH_CONFIG\0\u{1}SECURITY_CONFIG\0\u{1}SESSIONKEY_CONFIG\0\u{1}DEVICEUI_CONFIG\0") } extension AdminMessage.ModuleConfigType: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "MQTT_CONFIG"), - 1: .same(proto: "SERIAL_CONFIG"), - 2: .same(proto: "EXTNOTIF_CONFIG"), - 3: .same(proto: "STOREFORWARD_CONFIG"), - 4: .same(proto: "RANGETEST_CONFIG"), - 5: .same(proto: "TELEMETRY_CONFIG"), - 6: .same(proto: "CANNEDMSG_CONFIG"), - 7: .same(proto: "AUDIO_CONFIG"), - 8: .same(proto: "REMOTEHARDWARE_CONFIG"), - 9: .same(proto: "NEIGHBORINFO_CONFIG"), - 10: .same(proto: "AMBIENTLIGHTING_CONFIG"), - 11: .same(proto: "DETECTIONSENSOR_CONFIG"), - 12: .same(proto: "PAXCOUNTER_CONFIG"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0MQTT_CONFIG\0\u{1}SERIAL_CONFIG\0\u{1}EXTNOTIF_CONFIG\0\u{1}STOREFORWARD_CONFIG\0\u{1}RANGETEST_CONFIG\0\u{1}TELEMETRY_CONFIG\0\u{1}CANNEDMSG_CONFIG\0\u{1}AUDIO_CONFIG\0\u{1}REMOTEHARDWARE_CONFIG\0\u{1}NEIGHBORINFO_CONFIG\0\u{1}AMBIENTLIGHTING_CONFIG\0\u{1}DETECTIONSENSOR_CONFIG\0\u{1}PAXCOUNTER_CONFIG\0") } extension AdminMessage.BackupLocation: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "FLASH"), - 1: .same(proto: "SD"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0FLASH\0\u{1}SD\0") } extension AdminMessage.InputEvent: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = AdminMessage.protoMessageName + ".InputEvent" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "event_code"), - 2: .standard(proto: "kb_char"), - 3: .standard(proto: "touch_x"), - 4: .standard(proto: "touch_y"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}event_code\0\u{3}kb_char\0\u{3}touch_x\0\u{3}touch_y\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2416,12 +2074,7 @@ extension AdminMessage.InputEvent: SwiftProtobuf.Message, SwiftProtobuf._Message extension HamParameters: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".HamParameters" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "call_sign"), - 2: .standard(proto: "tx_power"), - 3: .same(proto: "frequency"), - 4: .standard(proto: "short_name"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}call_sign\0\u{3}tx_power\0\u{1}frequency\0\u{3}short_name\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2445,7 +2098,7 @@ extension HamParameters: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa if self.txPower != 0 { try visitor.visitSingularInt32Field(value: self.txPower, fieldNumber: 2) } - if self.frequency != 0 { + if self.frequency.bitPattern != 0 { try visitor.visitSingularFloatField(value: self.frequency, fieldNumber: 3) } if !self.shortName.isEmpty { @@ -2466,9 +2119,7 @@ extension HamParameters: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa extension NodeRemoteHardwarePinsResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".NodeRemoteHardwarePinsResponse" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "node_remote_hardware_pins"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}node_remote_hardware_pins\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2498,12 +2149,7 @@ extension NodeRemoteHardwarePinsResponse: SwiftProtobuf.Message, SwiftProtobuf._ extension SharedContact: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".SharedContact" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "node_num"), - 2: .same(proto: "user"), - 3: .standard(proto: "should_ignore"), - 4: .standard(proto: "manually_verified"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}node_num\0\u{1}user\0\u{3}should_ignore\0\u{3}manually_verified\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2552,12 +2198,7 @@ extension SharedContact: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa extension KeyVerificationAdmin: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".KeyVerificationAdmin" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "message_type"), - 2: .standard(proto: "remote_nodenum"), - 3: .same(proto: "nonce"), - 4: .standard(proto: "security_number"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}message_type\0\u{3}remote_nodenum\0\u{1}nonce\0\u{3}security_number\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2605,10 +2246,5 @@ extension KeyVerificationAdmin: SwiftProtobuf.Message, SwiftProtobuf._MessageImp } extension KeyVerificationAdmin.MessageType: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "INITIATE_VERIFICATION"), - 1: .same(proto: "PROVIDE_SECURITY_NUMBER"), - 2: .same(proto: "DO_VERIFY"), - 3: .same(proto: "DO_NOT_VERIFY"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0INITIATE_VERIFICATION\0\u{1}PROVIDE_SECURITY_NUMBER\0\u{1}DO_VERIFY\0\u{1}DO_NOT_VERIFY\0") } diff --git a/MeshtasticProtobufs/Sources/meshtastic/apponly.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/apponly.pb.swift index 0457077c..9ade8540 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/apponly.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/apponly.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/apponly.proto @@ -7,7 +8,6 @@ // 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 @@ -26,7 +26,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// any SECONDARY channels. /// No DISABLED channels are included. /// This abstraction is used only on the the 'app side' of the world (ie python, javascript and android etc) to show a group of Channels as a (long) URL -public struct ChannelSet { +public struct ChannelSet: Sendable { // 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. @@ -53,20 +53,13 @@ public struct ChannelSet { fileprivate var _loraConfig: Config.LoRaConfig? = nil } -#if swift(>=5.5) && canImport(_Concurrency) -extension ChannelSet: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" extension ChannelSet: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".ChannelSet" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "settings"), - 2: .standard(proto: "lora_config"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}settings\0\u{3}lora_config\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { diff --git a/MeshtasticProtobufs/Sources/meshtastic/atak.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/atak.pb.swift index 867648a9..3076a6da 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/atak.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/atak.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/atak.proto @@ -20,7 +21,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP typealias Version = _2 } -public enum Team: SwiftProtobuf.Enum { +public enum Team: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -130,11 +131,6 @@ public enum Team: SwiftProtobuf.Enum { } } -} - -#if swift(>=4.2) - -extension Team: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [Team] = [ .unspecifedColor, @@ -153,13 +149,12 @@ extension Team: CaseIterable { .darkGreen, .brown, ] -} -#endif // swift(>=4.2) +} /// /// Role of the group member -public enum MemberRole: SwiftProtobuf.Enum { +public enum MemberRole: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -233,11 +228,6 @@ public enum MemberRole: SwiftProtobuf.Enum { } } -} - -#if swift(>=4.2) - -extension MemberRole: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [MemberRole] = [ .unspecifed, @@ -250,13 +240,12 @@ extension MemberRole: CaseIterable { .rto, .k9, ] -} -#endif // swift(>=4.2) +} /// /// Packets for the official ATAK Plugin -public struct TAKPacket { +public struct TAKPacket: Sendable { // 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. @@ -337,7 +326,7 @@ public struct TAKPacket { /// /// The payload of the packet - public enum OneOf_PayloadVariant: Equatable { + public enum OneOf_PayloadVariant: Equatable, Sendable { /// /// TAK position report case pli(PLI) @@ -349,28 +338,6 @@ public struct TAKPacket { /// May be compressed / truncated by the sender (EUD) case detail(Data) - #if !swift(>=4.1) - public static func ==(lhs: TAKPacket.OneOf_PayloadVariant, rhs: TAKPacket.OneOf_PayloadVariant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.pli, .pli): return { - guard case .pli(let l) = lhs, case .pli(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.chat, .chat): return { - guard case .chat(let l) = lhs, case .chat(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.detail, .detail): return { - guard case .detail(let l) = lhs, case .detail(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } public init() {} @@ -382,7 +349,7 @@ public struct TAKPacket { /// /// ATAK GeoChat message -public struct GeoChat { +public struct GeoChat: Sendable { // 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. @@ -424,7 +391,7 @@ public struct GeoChat { /// /// ATAK Group /// <__group role='Team Member' name='Cyan'/> -public struct Group { +public struct Group: Sendable { // 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. @@ -446,7 +413,7 @@ public struct Group { /// /// ATAK EUD Status /// -public struct Status { +public struct Status: Sendable { // 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. @@ -463,7 +430,7 @@ public struct Status { /// /// ATAK Contact /// -public struct Contact { +public struct Contact: Sendable { // 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. @@ -483,7 +450,7 @@ public struct Contact { /// /// Position Location Information from ATAK -public struct PLI { +public struct PLI: Sendable { // 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. @@ -515,67 +482,21 @@ public struct PLI { public init() {} } -#if swift(>=5.5) && canImport(_Concurrency) -extension Team: @unchecked Sendable {} -extension MemberRole: @unchecked Sendable {} -extension TAKPacket: @unchecked Sendable {} -extension TAKPacket.OneOf_PayloadVariant: @unchecked Sendable {} -extension GeoChat: @unchecked Sendable {} -extension Group: @unchecked Sendable {} -extension Status: @unchecked Sendable {} -extension Contact: @unchecked Sendable {} -extension PLI: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" extension Team: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "Unspecifed_Color"), - 1: .same(proto: "White"), - 2: .same(proto: "Yellow"), - 3: .same(proto: "Orange"), - 4: .same(proto: "Magenta"), - 5: .same(proto: "Red"), - 6: .same(proto: "Maroon"), - 7: .same(proto: "Purple"), - 8: .same(proto: "Dark_Blue"), - 9: .same(proto: "Blue"), - 10: .same(proto: "Cyan"), - 11: .same(proto: "Teal"), - 12: .same(proto: "Green"), - 13: .same(proto: "Dark_Green"), - 14: .same(proto: "Brown"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0Unspecifed_Color\0\u{1}White\0\u{1}Yellow\0\u{1}Orange\0\u{1}Magenta\0\u{1}Red\0\u{1}Maroon\0\u{1}Purple\0\u{1}Dark_Blue\0\u{1}Blue\0\u{1}Cyan\0\u{1}Teal\0\u{1}Green\0\u{1}Dark_Green\0\u{1}Brown\0") } extension MemberRole: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "Unspecifed"), - 1: .same(proto: "TeamMember"), - 2: .same(proto: "TeamLead"), - 3: .same(proto: "HQ"), - 4: .same(proto: "Sniper"), - 5: .same(proto: "Medic"), - 6: .same(proto: "ForwardObserver"), - 7: .same(proto: "RTO"), - 8: .same(proto: "K9"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0Unspecifed\0\u{1}TeamMember\0\u{1}TeamLead\0\u{1}HQ\0\u{1}Sniper\0\u{1}Medic\0\u{1}ForwardObserver\0\u{1}RTO\0\u{1}K9\0") } extension TAKPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".TAKPacket" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "is_compressed"), - 2: .same(proto: "contact"), - 3: .same(proto: "group"), - 4: .same(proto: "status"), - 5: .same(proto: "pli"), - 6: .same(proto: "chat"), - 7: .same(proto: "detail"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}is_compressed\0\u{1}contact\0\u{1}group\0\u{1}status\0\u{1}pli\0\u{1}chat\0\u{1}detail\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -674,11 +595,7 @@ extension TAKPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation extension GeoChat: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".GeoChat" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "message"), - 2: .same(proto: "to"), - 3: .standard(proto: "to_callsign"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}message\0\u{1}to\0\u{3}to_callsign\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -722,10 +639,7 @@ extension GeoChat: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBa extension Group: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Group" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "role"), - 2: .same(proto: "team"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}role\0\u{1}team\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -760,9 +674,7 @@ extension Group: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase extension Status: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Status" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "battery"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}battery\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -792,10 +704,7 @@ extension Status: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBas extension Contact: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Contact" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "callsign"), - 2: .standard(proto: "device_callsign"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}callsign\0\u{3}device_callsign\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -830,13 +739,7 @@ extension Contact: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBa extension PLI: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".PLI" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "latitude_i"), - 2: .standard(proto: "longitude_i"), - 3: .same(proto: "altitude"), - 4: .same(proto: "speed"), - 5: .same(proto: "course"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}latitude_i\0\u{3}longitude_i\0\u{1}altitude\0\u{1}speed\0\u{1}course\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { diff --git a/MeshtasticProtobufs/Sources/meshtastic/cannedmessages.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/cannedmessages.pb.swift index 1b8c84de..8776adbf 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/cannedmessages.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/cannedmessages.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/cannedmessages.proto @@ -7,7 +8,6 @@ // 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 @@ -22,7 +22,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// /// Canned message module configuration. -public struct CannedMessageModuleConfig { +public struct CannedMessageModuleConfig: Sendable { // 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. @@ -36,19 +36,13 @@ public struct CannedMessageModuleConfig { public init() {} } -#if swift(>=5.5) && canImport(_Concurrency) -extension CannedMessageModuleConfig: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" extension CannedMessageModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".CannedMessageModuleConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "messages"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}messages\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { diff --git a/MeshtasticProtobufs/Sources/meshtastic/channel.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/channel.pb.swift index 9af4a87a..15995842 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/channel.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/channel.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/channel.proto @@ -36,13 +37,15 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// FIXME: Add description of multi-channel support and how primary vs secondary channels are used. /// FIXME: explain how apps use channels for security. /// explain how remote settings and remote gpio are managed as an example -public struct ChannelSettings { +public struct ChannelSettings: Sendable { // 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. /// /// Deprecated in favor of LoraConfig.channel_num + /// + /// NOTE: This field was marked as deprecated in the .proto file. public var channelNum: UInt32 = 0 /// @@ -102,10 +105,6 @@ public struct ChannelSettings { /// Clears the value of `moduleSettings`. Subsequent reads from it will return its default value. public mutating func clearModuleSettings() {self._moduleSettings = nil} - /// - /// Whether or not we should receive notifactions / alerts through this channel - public var mute: Bool = false - public var unknownFields = SwiftProtobuf.UnknownStorage() public init() {} @@ -115,7 +114,7 @@ public struct ChannelSettings { /// /// This message is specifically for modules to store per-channel configuration data. -public struct ModuleSettings { +public struct ModuleSettings: Sendable { // 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. @@ -125,9 +124,9 @@ public struct ModuleSettings { public var positionPrecision: UInt32 = 0 /// - /// Controls whether or not the phone / clients should mute the current channel + /// Controls whether or not the client / device should mute the current channel /// Useful for noisy public channels you don't necessarily want to disable - public var isClientMuted: Bool = false + public var isMuted: Bool = false public var unknownFields = SwiftProtobuf.UnknownStorage() @@ -136,7 +135,7 @@ public struct ModuleSettings { /// /// A pair of a channel number, mode and the (sharable) settings for that channel -public struct Channel { +public struct Channel: Sendable { // 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. @@ -174,7 +173,7 @@ public struct Channel { /// cross band routing as needed. /// If a device has only a single radio (the common case) only one channel can be PRIMARY at a time /// (but any number of SECONDARY channels can't be sent received on that common frequency) - public enum Role: SwiftProtobuf.Enum { + public enum Role: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -213,6 +212,13 @@ public struct Channel { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Channel.Role] = [ + .disabled, + .primary, + .secondary, + ] + } public init() {} @@ -220,42 +226,13 @@ public struct Channel { fileprivate var _settings: ChannelSettings? = nil } -#if swift(>=4.2) - -extension Channel.Role: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Channel.Role] = [ - .disabled, - .primary, - .secondary, - ] -} - -#endif // swift(>=4.2) - -#if swift(>=5.5) && canImport(_Concurrency) -extension ChannelSettings: @unchecked Sendable {} -extension ModuleSettings: @unchecked Sendable {} -extension Channel: @unchecked Sendable {} -extension Channel.Role: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" extension ChannelSettings: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".ChannelSettings" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "channel_num"), - 2: .same(proto: "psk"), - 3: .same(proto: "name"), - 4: .same(proto: "id"), - 5: .standard(proto: "uplink_enabled"), - 6: .standard(proto: "downlink_enabled"), - 7: .standard(proto: "module_settings"), - 8: .same(proto: "mute"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}channel_num\0\u{1}psk\0\u{1}name\0\u{1}id\0\u{3}uplink_enabled\0\u{3}downlink_enabled\0\u{3}module_settings\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -270,7 +247,6 @@ extension ChannelSettings: SwiftProtobuf.Message, SwiftProtobuf._MessageImplemen case 5: try { try decoder.decodeSingularBoolField(value: &self.uplinkEnabled) }() case 6: try { try decoder.decodeSingularBoolField(value: &self.downlinkEnabled) }() case 7: try { try decoder.decodeSingularMessageField(value: &self._moduleSettings) }() - case 8: try { try decoder.decodeSingularBoolField(value: &self.mute) }() default: break } } @@ -302,9 +278,6 @@ extension ChannelSettings: SwiftProtobuf.Message, SwiftProtobuf._MessageImplemen try { if let v = self._moduleSettings { try visitor.visitSingularMessageField(value: v, fieldNumber: 7) } }() - if self.mute != false { - try visitor.visitSingularBoolField(value: self.mute, fieldNumber: 8) - } try unknownFields.traverse(visitor: &visitor) } @@ -316,7 +289,6 @@ extension ChannelSettings: SwiftProtobuf.Message, SwiftProtobuf._MessageImplemen if lhs.uplinkEnabled != rhs.uplinkEnabled {return false} if lhs.downlinkEnabled != rhs.downlinkEnabled {return false} if lhs._moduleSettings != rhs._moduleSettings {return false} - if lhs.mute != rhs.mute {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } @@ -324,10 +296,7 @@ extension ChannelSettings: SwiftProtobuf.Message, SwiftProtobuf._MessageImplemen extension ModuleSettings: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".ModuleSettings" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "position_precision"), - 2: .standard(proto: "is_client_muted"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}position_precision\0\u{3}is_muted\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -336,7 +305,7 @@ extension ModuleSettings: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement // enabled. https://github.com/apple/swift-protobuf/issues/1034 switch fieldNumber { case 1: try { try decoder.decodeSingularUInt32Field(value: &self.positionPrecision) }() - case 2: try { try decoder.decodeSingularBoolField(value: &self.isClientMuted) }() + case 2: try { try decoder.decodeSingularBoolField(value: &self.isMuted) }() default: break } } @@ -346,15 +315,15 @@ extension ModuleSettings: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement if self.positionPrecision != 0 { try visitor.visitSingularUInt32Field(value: self.positionPrecision, fieldNumber: 1) } - if self.isClientMuted != false { - try visitor.visitSingularBoolField(value: self.isClientMuted, fieldNumber: 2) + if self.isMuted != false { + try visitor.visitSingularBoolField(value: self.isMuted, fieldNumber: 2) } try unknownFields.traverse(visitor: &visitor) } public static func ==(lhs: ModuleSettings, rhs: ModuleSettings) -> Bool { if lhs.positionPrecision != rhs.positionPrecision {return false} - if lhs.isClientMuted != rhs.isClientMuted {return false} + if lhs.isMuted != rhs.isMuted {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } @@ -362,11 +331,7 @@ extension ModuleSettings: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement extension Channel: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Channel" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "index"), - 2: .same(proto: "settings"), - 3: .same(proto: "role"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}index\0\u{1}settings\0\u{1}role\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -409,9 +374,5 @@ extension Channel: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBa } extension Channel.Role: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "DISABLED"), - 1: .same(proto: "PRIMARY"), - 2: .same(proto: "SECONDARY"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0DISABLED\0\u{1}PRIMARY\0\u{1}SECONDARY\0") } diff --git a/MeshtasticProtobufs/Sources/meshtastic/clientonly.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/clientonly.pb.swift index f89a8e3c..9afbdf9c 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/clientonly.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/clientonly.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/clientonly.proto @@ -7,7 +8,6 @@ // 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 @@ -23,7 +23,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// /// This abstraction is used to contain any configuration for provisioning a node on any client. /// It is useful for importing and exporting configurations. -public struct DeviceProfile { +public struct DeviceProfile: Sendable { // 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. @@ -130,26 +130,13 @@ public struct DeviceProfile { fileprivate var _cannedMessages: String? = 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 { public static let protoMessageName: String = _protobuf_package + ".DeviceProfile" - public 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"), - 6: .standard(proto: "fixed_position"), - 7: .same(proto: "ringtone"), - 8: .standard(proto: "canned_messages"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}long_name\0\u{3}short_name\0\u{3}channel_url\0\u{1}config\0\u{3}module_config\0\u{3}fixed_position\0\u{1}ringtone\0\u{3}canned_messages\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { diff --git a/MeshtasticProtobufs/Sources/meshtastic/config.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/config.pb.swift index 03862795..28074a6b 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/config.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/config.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/config.proto @@ -20,7 +21,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP typealias Version = _2 } -public struct Config { +public struct Config: Sendable { // 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. @@ -113,7 +114,7 @@ public struct Config { /// /// Payload Variant - public enum OneOf_PayloadVariant: Equatable { + public enum OneOf_PayloadVariant: Equatable, Sendable { case device(Config.DeviceConfig) case position(Config.PositionConfig) case power(Config.PowerConfig) @@ -125,61 +126,11 @@ public struct Config { case sessionkey(Config.SessionkeyConfig) case deviceUi(DeviceUIConfig) - #if !swift(>=4.1) - public static func ==(lhs: Config.OneOf_PayloadVariant, rhs: Config.OneOf_PayloadVariant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.device, .device): return { - guard case .device(let l) = lhs, case .device(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.position, .position): return { - guard case .position(let l) = lhs, case .position(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.power, .power): return { - guard case .power(let l) = lhs, case .power(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.network, .network): return { - guard case .network(let l) = lhs, case .network(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.display, .display): return { - guard case .display(let l) = lhs, case .display(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.lora, .lora): return { - guard case .lora(let l) = lhs, case .lora(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.bluetooth, .bluetooth): return { - guard case .bluetooth(let l) = lhs, case .bluetooth(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.security, .security): return { - guard case .security(let l) = lhs, case .security(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.sessionkey, .sessionkey): return { - guard case .sessionkey(let l) = lhs, case .sessionkey(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.deviceUi, .deviceUi): return { - guard case .deviceUi(let l) = lhs, case .deviceUi(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } /// /// Configuration - public struct DeviceConfig { + public struct DeviceConfig: Sendable { // 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. @@ -191,6 +142,8 @@ public struct Config { /// /// Disabling this will disable the SerialConsole by not initilizing the StreamAPI /// Moved to SecurityConfig + /// + /// NOTE: This field was marked as deprecated in the .proto file. public var serialEnabled: Bool = false /// @@ -220,6 +173,8 @@ public struct Config { /// If true, device is considered to be "managed" by a mesh administrator /// Clients should then limit available configuration and administrative options inside the user interface /// Moved to SecurityConfig + /// + /// NOTE: This field was marked as deprecated in the .proto file. public var isManaged: Bool = false /// @@ -243,7 +198,7 @@ public struct Config { /// /// Defines the device's role on the Mesh network - public enum Role: SwiftProtobuf.Enum { + public enum Role: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -261,6 +216,8 @@ public struct Config { /// The wifi radio and the oled screen will be put to sleep. /// This mode may still potentially have higher power usage due to it's preference in message rebroadcasting on the mesh. case router // = 2 + + /// NOTE: This enum value was marked as deprecated in the .proto file case routerClient // = 3 /// @@ -268,6 +225,8 @@ public struct Config { /// Technical Details: Mesh packets will simply be rebroadcasted over this node. Nodes configured with this role will not originate NodeInfo, Position, Telemetry /// or any other packet type. They will simply rebroadcast any mesh packets on the same frequency, channel num, spread factor, and coding rate. /// Deprecated in v2.7.11 because it creates "holes" in the mesh rebroadcast chain. + /// + /// NOTE: This enum value was marked as deprecated in the .proto file case repeater // = 4 /// @@ -371,11 +330,28 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.DeviceConfig.Role] = [ + .client, + .clientMute, + .router, + .routerClient, + .repeater, + .tracker, + .sensor, + .tak, + .clientHidden, + .lostAndFound, + .takTracker, + .routerLate, + .clientBase, + ] + } /// /// Defines the device's behavior for how messages are rebroadcast - public enum RebroadcastMode: SwiftProtobuf.Enum { + public enum RebroadcastMode: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -436,11 +412,21 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.DeviceConfig.RebroadcastMode] = [ + .all, + .allSkipDecoding, + .localOnly, + .knownOnly, + .none, + .corePortnumsOnly, + ] + } /// /// Defines buzzer behavior for audio feedback - public enum BuzzerMode: SwiftProtobuf.Enum { + public enum BuzzerMode: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -497,6 +483,15 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.DeviceConfig.BuzzerMode] = [ + .allEnabled, + .disabled, + .notificationsOnly, + .systemOnly, + .directMsgOnly, + ] + } public init() {} @@ -504,7 +499,7 @@ public struct Config { /// /// Position Config - public struct PositionConfig { + public struct PositionConfig: Sendable { // 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. @@ -526,6 +521,8 @@ public struct Config { /// /// Is GPS enabled for this node? + /// + /// NOTE: This field was marked as deprecated in the .proto file. public var gpsEnabled: Bool = false /// @@ -536,6 +533,8 @@ public struct Config { /// /// Deprecated in favor of using smart / regular broadcast intervals as implicit attempt time + /// + /// NOTE: This field was marked as deprecated in the .proto file. public var gpsAttemptTime: UInt32 = 0 /// @@ -576,7 +575,7 @@ public struct Config { /// are always included (also time if GPS-synced) /// NOTE: the more fields are included, the larger the message will be - /// leading to longer airtime and a higher risk of packet loss - public enum PositionFlags: SwiftProtobuf.Enum { + public enum PositionFlags: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -666,9 +665,24 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.PositionConfig.PositionFlags] = [ + .unset, + .altitude, + .altitudeMsl, + .geoidalSeparation, + .dop, + .hvdop, + .satinview, + .seqNo, + .timestamp, + .heading, + .speed, + ] + } - public enum GpsMode: SwiftProtobuf.Enum { + public enum GpsMode: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -706,6 +720,13 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.PositionConfig.GpsMode] = [ + .disabled, + .enabled, + .notPresent, + ] + } public init() {} @@ -714,7 +735,7 @@ public struct Config { /// /// Power Config\ /// See [Power Config](/docs/settings/config/power) for additional power config details. - public struct PowerConfig { + public struct PowerConfig: Sendable { // 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. @@ -774,7 +795,7 @@ public struct Config { /// /// Network Config - public struct NetworkConfig { + public struct NetworkConfig: Sendable { // 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. @@ -829,7 +850,7 @@ public struct Config { public var unknownFields = SwiftProtobuf.UnknownStorage() - public enum AddressMode: SwiftProtobuf.Enum { + public enum AddressMode: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -861,11 +882,17 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.NetworkConfig.AddressMode] = [ + .dhcp, + .static, + ] + } /// /// Available flags auxiliary network protocols - public enum ProtocolFlags: SwiftProtobuf.Enum { + public enum ProtocolFlags: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -897,9 +924,15 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.NetworkConfig.ProtocolFlags] = [ + .noBroadcast, + .udpBroadcast, + ] + } - public struct IpV4Config { + 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 // methods supported on all messages. @@ -932,7 +965,7 @@ public struct Config { /// /// Display Config - public struct DisplayConfig { + public struct DisplayConfig: Sendable { // 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. @@ -945,6 +978,8 @@ public struct Config { /// /// Deprecated in 2.7.4: Unused /// How the GPS coordinates are formatted on the OLED screen. + /// + /// NOTE: This field was marked as deprecated in the .proto file. public var gpsFormat: Config.DisplayConfig.DeprecatedGpsCoordinateFormat = .unused /// @@ -955,6 +990,8 @@ public struct Config { /// /// If this is set, the displayed compass will always point north. if unset, the old behaviour /// (top of display is heading direction) is used. + /// + /// NOTE: This field was marked as deprecated in the .proto file. public var compassNorthTop: Bool = false /// @@ -999,7 +1036,7 @@ public struct Config { /// /// Deprecated in 2.7.4: Unused - public enum DeprecatedGpsCoordinateFormat: SwiftProtobuf.Enum { + public enum DeprecatedGpsCoordinateFormat: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int case unused // = 0 case UNRECOGNIZED(Int) @@ -1022,11 +1059,16 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.DisplayConfig.DeprecatedGpsCoordinateFormat] = [ + .unused, + ] + } /// /// Unit display preference - public enum DisplayUnits: SwiftProtobuf.Enum { + public enum DisplayUnits: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1058,11 +1100,17 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.DisplayConfig.DisplayUnits] = [ + .metric, + .imperial, + ] + } /// /// Override OLED outo detect with this if it fails. - public enum OledType: SwiftProtobuf.Enum { + public enum OledType: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1112,9 +1160,18 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.DisplayConfig.OledType] = [ + .oledAuto, + .oledSsd1306, + .oledSh1106, + .oledSh1107, + .oledSh1107128128, + ] + } - public enum DisplayMode: SwiftProtobuf.Enum { + public enum DisplayMode: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1158,9 +1215,17 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.DisplayConfig.DisplayMode] = [ + .default, + .twocolor, + .inverted, + .color, + ] + } - public enum CompassOrientation: SwiftProtobuf.Enum { + public enum CompassOrientation: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1228,6 +1293,18 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.DisplayConfig.CompassOrientation] = [ + .degrees0, + .degrees90, + .degrees180, + .degrees270, + .degrees0Inverted, + .degrees90Inverted, + .degrees180Inverted, + .degrees270Inverted, + ] + } public init() {} @@ -1235,7 +1312,7 @@ public struct Config { /// /// Lora Config - public struct LoRaConfig { + public struct LoRaConfig: @unchecked Sendable { // 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. @@ -1399,7 +1476,7 @@ public struct Config { public var unknownFields = SwiftProtobuf.UnknownStorage() - public enum RegionCode: SwiftProtobuf.Enum { + public enum RegionCode: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1581,12 +1658,43 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.LoRaConfig.RegionCode] = [ + .unset, + .us, + .eu433, + .eu868, + .cn, + .jp, + .anz, + .kr, + .tw, + .ru, + .in, + .nz865, + .th, + .lora24, + .ua433, + .ua868, + .my433, + .my919, + .sg923, + .ph433, + .ph868, + .ph915, + .anz433, + .kz433, + .kz863, + .np865, + .br902, + ] + } /// /// Standard predefined channel settings /// Note: these mappings must match ModemPreset Choice in the device code. - public enum ModemPreset: SwiftProtobuf.Enum { + public enum ModemPreset: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1595,11 +1703,16 @@ public struct Config { /// /// Long Range - Slow + /// Deprecated in 2.7: Unpopular slow preset. + /// + /// NOTE: This enum value was marked as deprecated in the .proto file case longSlow // = 1 /// /// Very Long Range - Slow /// Deprecated in 2.5: Works only with txco and is unusably slow + /// + /// NOTE: This enum value was marked as deprecated in the .proto file case veryLongSlow // = 2 /// @@ -1627,6 +1740,11 @@ public struct Config { /// This is the fastest preset and the only one with 500kHz bandwidth. /// It is not legal to use in all regions due to this wider bandwidth. case shortTurbo // = 8 + + /// + /// Long Range - Turbo + /// This preset performs similarly to LongFast, but with 500Khz bandwidth. + case longTurbo // = 9 case UNRECOGNIZED(Int) public init() { @@ -1644,6 +1762,7 @@ public struct Config { case 6: self = .shortFast case 7: self = .longModerate case 8: self = .shortTurbo + case 9: self = .longTurbo default: self = .UNRECOGNIZED(rawValue) } } @@ -1659,10 +1778,25 @@ public struct Config { case .shortFast: return 6 case .longModerate: return 7 case .shortTurbo: return 8 + case .longTurbo: return 9 case .UNRECOGNIZED(let i): return i } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.LoRaConfig.ModemPreset] = [ + .longFast, + .longSlow, + .veryLongSlow, + .mediumSlow, + .mediumFast, + .shortSlow, + .shortFast, + .longModerate, + .shortTurbo, + .longTurbo, + ] + } public init() {} @@ -1670,7 +1804,7 @@ public struct Config { fileprivate var _storage = _StorageClass.defaultInstance } - public struct BluetoothConfig { + public struct BluetoothConfig: Sendable { // 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. @@ -1689,7 +1823,7 @@ public struct Config { public var unknownFields = SwiftProtobuf.UnknownStorage() - public enum PairingMode: SwiftProtobuf.Enum { + public enum PairingMode: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1727,12 +1861,19 @@ public struct Config { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.BluetoothConfig.PairingMode] = [ + .randomPin, + .fixedPin, + .noPin, + ] + } public init() {} } - public struct SecurityConfig { + public struct SecurityConfig: Sendable { // 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. @@ -1776,7 +1917,7 @@ public struct Config { /// /// Blank config request, strictly for getting the session key - public struct SessionkeyConfig { + public struct SessionkeyConfig: Sendable { // 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. @@ -1789,249 +1930,13 @@ public struct Config { public init() {} } -#if swift(>=4.2) - -extension Config.DeviceConfig.Role: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.DeviceConfig.Role] = [ - .client, - .clientMute, - .router, - .routerClient, - .repeater, - .tracker, - .sensor, - .tak, - .clientHidden, - .lostAndFound, - .takTracker, - .routerLate, - .clientBase, - ] -} - -extension Config.DeviceConfig.RebroadcastMode: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.DeviceConfig.RebroadcastMode] = [ - .all, - .allSkipDecoding, - .localOnly, - .knownOnly, - .none, - .corePortnumsOnly, - ] -} - -extension Config.DeviceConfig.BuzzerMode: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.DeviceConfig.BuzzerMode] = [ - .allEnabled, - .disabled, - .notificationsOnly, - .systemOnly, - .directMsgOnly, - ] -} - -extension Config.PositionConfig.PositionFlags: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.PositionConfig.PositionFlags] = [ - .unset, - .altitude, - .altitudeMsl, - .geoidalSeparation, - .dop, - .hvdop, - .satinview, - .seqNo, - .timestamp, - .heading, - .speed, - ] -} - -extension Config.PositionConfig.GpsMode: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.PositionConfig.GpsMode] = [ - .disabled, - .enabled, - .notPresent, - ] -} - -extension Config.NetworkConfig.AddressMode: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.NetworkConfig.AddressMode] = [ - .dhcp, - .static, - ] -} - -extension Config.NetworkConfig.ProtocolFlags: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.NetworkConfig.ProtocolFlags] = [ - .noBroadcast, - .udpBroadcast, - ] -} - -extension Config.DisplayConfig.DeprecatedGpsCoordinateFormat: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.DisplayConfig.DeprecatedGpsCoordinateFormat] = [ - .unused, - ] -} - -extension Config.DisplayConfig.DisplayUnits: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.DisplayConfig.DisplayUnits] = [ - .metric, - .imperial, - ] -} - -extension Config.DisplayConfig.OledType: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.DisplayConfig.OledType] = [ - .oledAuto, - .oledSsd1306, - .oledSh1106, - .oledSh1107, - .oledSh1107128128, - ] -} - -extension Config.DisplayConfig.DisplayMode: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.DisplayConfig.DisplayMode] = [ - .default, - .twocolor, - .inverted, - .color, - ] -} - -extension Config.DisplayConfig.CompassOrientation: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.DisplayConfig.CompassOrientation] = [ - .degrees0, - .degrees90, - .degrees180, - .degrees270, - .degrees0Inverted, - .degrees90Inverted, - .degrees180Inverted, - .degrees270Inverted, - ] -} - -extension Config.LoRaConfig.RegionCode: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.LoRaConfig.RegionCode] = [ - .unset, - .us, - .eu433, - .eu868, - .cn, - .jp, - .anz, - .kr, - .tw, - .ru, - .in, - .nz865, - .th, - .lora24, - .ua433, - .ua868, - .my433, - .my919, - .sg923, - .ph433, - .ph868, - .ph915, - .anz433, - .kz433, - .kz863, - .np865, - .br902, - ] -} - -extension Config.LoRaConfig.ModemPreset: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.LoRaConfig.ModemPreset] = [ - .longFast, - .longSlow, - .veryLongSlow, - .mediumSlow, - .mediumFast, - .shortSlow, - .shortFast, - .longModerate, - .shortTurbo, - ] -} - -extension Config.BluetoothConfig.PairingMode: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Config.BluetoothConfig.PairingMode] = [ - .randomPin, - .fixedPin, - .noPin, - ] -} - -#endif // swift(>=4.2) - -#if swift(>=5.5) && canImport(_Concurrency) -extension Config: @unchecked Sendable {} -extension Config.OneOf_PayloadVariant: @unchecked Sendable {} -extension Config.DeviceConfig: @unchecked Sendable {} -extension Config.DeviceConfig.Role: @unchecked Sendable {} -extension Config.DeviceConfig.RebroadcastMode: @unchecked Sendable {} -extension Config.DeviceConfig.BuzzerMode: @unchecked Sendable {} -extension Config.PositionConfig: @unchecked Sendable {} -extension Config.PositionConfig.PositionFlags: @unchecked Sendable {} -extension Config.PositionConfig.GpsMode: @unchecked Sendable {} -extension Config.PowerConfig: @unchecked Sendable {} -extension Config.NetworkConfig: @unchecked Sendable {} -extension Config.NetworkConfig.AddressMode: @unchecked Sendable {} -extension Config.NetworkConfig.ProtocolFlags: @unchecked Sendable {} -extension Config.NetworkConfig.IpV4Config: @unchecked Sendable {} -extension Config.DisplayConfig: @unchecked Sendable {} -extension Config.DisplayConfig.DeprecatedGpsCoordinateFormat: @unchecked Sendable {} -extension Config.DisplayConfig.DisplayUnits: @unchecked Sendable {} -extension Config.DisplayConfig.OledType: @unchecked Sendable {} -extension Config.DisplayConfig.DisplayMode: @unchecked Sendable {} -extension Config.DisplayConfig.CompassOrientation: @unchecked Sendable {} -extension Config.LoRaConfig: @unchecked Sendable {} -extension Config.LoRaConfig.RegionCode: @unchecked Sendable {} -extension Config.LoRaConfig.ModemPreset: @unchecked Sendable {} -extension Config.BluetoothConfig: @unchecked Sendable {} -extension Config.BluetoothConfig.PairingMode: @unchecked Sendable {} -extension Config.SecurityConfig: @unchecked Sendable {} -extension Config.SessionkeyConfig: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" extension Config: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Config" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "device"), - 2: .same(proto: "position"), - 3: .same(proto: "power"), - 4: .same(proto: "network"), - 5: .same(proto: "display"), - 6: .same(proto: "lora"), - 7: .same(proto: "bluetooth"), - 8: .same(proto: "security"), - 9: .same(proto: "sessionkey"), - 10: .standard(proto: "device_ui"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}device\0\u{1}position\0\u{1}power\0\u{1}network\0\u{1}display\0\u{1}lora\0\u{1}bluetooth\0\u{1}security\0\u{1}sessionkey\0\u{3}device_ui\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2234,20 +2139,7 @@ extension Config: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBas extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = Config.protoMessageName + ".DeviceConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "role"), - 2: .standard(proto: "serial_enabled"), - 4: .standard(proto: "button_gpio"), - 5: .standard(proto: "buzzer_gpio"), - 6: .standard(proto: "rebroadcast_mode"), - 7: .standard(proto: "node_info_broadcast_secs"), - 8: .standard(proto: "double_tap_as_button_press"), - 9: .standard(proto: "is_managed"), - 10: .standard(proto: "disable_triple_click"), - 11: .same(proto: "tzdef"), - 12: .standard(proto: "led_heartbeat_disabled"), - 13: .standard(proto: "buzzer_mode"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}role\0\u{3}serial_enabled\0\u{4}\u{2}button_gpio\0\u{3}buzzer_gpio\0\u{3}rebroadcast_mode\0\u{3}node_info_broadcast_secs\0\u{3}double_tap_as_button_press\0\u{3}is_managed\0\u{3}disable_triple_click\0\u{1}tzdef\0\u{3}led_heartbeat_disabled\0\u{3}buzzer_mode\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2331,61 +2223,20 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl } extension Config.DeviceConfig.Role: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "CLIENT"), - 1: .same(proto: "CLIENT_MUTE"), - 2: .same(proto: "ROUTER"), - 3: .same(proto: "ROUTER_CLIENT"), - 4: .same(proto: "REPEATER"), - 5: .same(proto: "TRACKER"), - 6: .same(proto: "SENSOR"), - 7: .same(proto: "TAK"), - 8: .same(proto: "CLIENT_HIDDEN"), - 9: .same(proto: "LOST_AND_FOUND"), - 10: .same(proto: "TAK_TRACKER"), - 11: .same(proto: "ROUTER_LATE"), - 12: .same(proto: "CLIENT_BASE"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0CLIENT\0\u{1}CLIENT_MUTE\0\u{1}ROUTER\0\u{1}ROUTER_CLIENT\0\u{1}REPEATER\0\u{1}TRACKER\0\u{1}SENSOR\0\u{1}TAK\0\u{1}CLIENT_HIDDEN\0\u{1}LOST_AND_FOUND\0\u{1}TAK_TRACKER\0\u{1}ROUTER_LATE\0\u{1}CLIENT_BASE\0") } extension Config.DeviceConfig.RebroadcastMode: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "ALL"), - 1: .same(proto: "ALL_SKIP_DECODING"), - 2: .same(proto: "LOCAL_ONLY"), - 3: .same(proto: "KNOWN_ONLY"), - 4: .same(proto: "NONE"), - 5: .same(proto: "CORE_PORTNUMS_ONLY"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0ALL\0\u{1}ALL_SKIP_DECODING\0\u{1}LOCAL_ONLY\0\u{1}KNOWN_ONLY\0\u{1}NONE\0\u{1}CORE_PORTNUMS_ONLY\0") } extension Config.DeviceConfig.BuzzerMode: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "ALL_ENABLED"), - 1: .same(proto: "DISABLED"), - 2: .same(proto: "NOTIFICATIONS_ONLY"), - 3: .same(proto: "SYSTEM_ONLY"), - 4: .same(proto: "DIRECT_MSG_ONLY"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0ALL_ENABLED\0\u{1}DISABLED\0\u{1}NOTIFICATIONS_ONLY\0\u{1}SYSTEM_ONLY\0\u{1}DIRECT_MSG_ONLY\0") } extension Config.PositionConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = Config.protoMessageName + ".PositionConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "position_broadcast_secs"), - 2: .standard(proto: "position_broadcast_smart_enabled"), - 3: .standard(proto: "fixed_position"), - 4: .standard(proto: "gps_enabled"), - 5: .standard(proto: "gps_update_interval"), - 6: .standard(proto: "gps_attempt_time"), - 7: .standard(proto: "position_flags"), - 8: .standard(proto: "rx_gpio"), - 9: .standard(proto: "tx_gpio"), - 10: .standard(proto: "broadcast_smart_minimum_distance"), - 11: .standard(proto: "broadcast_smart_minimum_interval_secs"), - 12: .standard(proto: "gps_en_gpio"), - 13: .standard(proto: "gps_mode"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}position_broadcast_secs\0\u{3}position_broadcast_smart_enabled\0\u{3}fixed_position\0\u{3}gps_enabled\0\u{3}gps_update_interval\0\u{3}gps_attempt_time\0\u{3}position_flags\0\u{3}rx_gpio\0\u{3}tx_gpio\0\u{3}broadcast_smart_minimum_distance\0\u{3}broadcast_smart_minimum_interval_secs\0\u{3}gps_en_gpio\0\u{3}gps_mode\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2474,42 +2325,16 @@ extension Config.PositionConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageIm } extension Config.PositionConfig.PositionFlags: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "UNSET"), - 1: .same(proto: "ALTITUDE"), - 2: .same(proto: "ALTITUDE_MSL"), - 4: .same(proto: "GEOIDAL_SEPARATION"), - 8: .same(proto: "DOP"), - 16: .same(proto: "HVDOP"), - 32: .same(proto: "SATINVIEW"), - 64: .same(proto: "SEQ_NO"), - 128: .same(proto: "TIMESTAMP"), - 256: .same(proto: "HEADING"), - 512: .same(proto: "SPEED"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0UNSET\0\u{1}ALTITUDE\0\u{1}ALTITUDE_MSL\0\u{2}\u{2}GEOIDAL_SEPARATION\0\u{2}\u{4}DOP\0\u{2}\u{8}HVDOP\0\u{2}\u{10}SATINVIEW\0\u{2} SEQ_NO\0\u{2}@\u{1}TIMESTAMP\0\u{2}@\u{2}HEADING\0\u{2}@\u{4}SPEED\0") } extension Config.PositionConfig.GpsMode: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "DISABLED"), - 1: .same(proto: "ENABLED"), - 2: .same(proto: "NOT_PRESENT"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0DISABLED\0\u{1}ENABLED\0\u{1}NOT_PRESENT\0") } extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = Config.protoMessageName + ".PowerConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "is_power_saving"), - 2: .standard(proto: "on_battery_shutdown_after_secs"), - 3: .standard(proto: "adc_multiplier_override"), - 4: .standard(proto: "wait_bluetooth_secs"), - 6: .standard(proto: "sds_secs"), - 7: .standard(proto: "ls_secs"), - 8: .standard(proto: "min_wake_secs"), - 9: .standard(proto: "device_battery_ina_address"), - 32: .standard(proto: "powermon_enables"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}is_power_saving\0\u{3}on_battery_shutdown_after_secs\0\u{3}adc_multiplier_override\0\u{3}wait_bluetooth_secs\0\u{4}\u{2}sds_secs\0\u{3}ls_secs\0\u{3}min_wake_secs\0\u{3}device_battery_ina_address\0\u{4}\u{17}powermon_enables\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2538,7 +2363,7 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple if self.onBatteryShutdownAfterSecs != 0 { try visitor.visitSingularUInt32Field(value: self.onBatteryShutdownAfterSecs, fieldNumber: 2) } - if self.adcMultiplierOverride != 0 { + if self.adcMultiplierOverride.bitPattern != 0 { try visitor.visitSingularFloatField(value: self.adcMultiplierOverride, fieldNumber: 3) } if self.waitBluetoothSecs != 0 { @@ -2579,18 +2404,7 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple extension Config.NetworkConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = Config.protoMessageName + ".NetworkConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "wifi_enabled"), - 3: .standard(proto: "wifi_ssid"), - 4: .standard(proto: "wifi_psk"), - 5: .standard(proto: "ntp_server"), - 6: .standard(proto: "eth_enabled"), - 7: .standard(proto: "address_mode"), - 8: .standard(proto: "ipv4_config"), - 9: .standard(proto: "rsyslog_server"), - 10: .standard(proto: "enabled_protocols"), - 11: .standard(proto: "ipv6_enabled"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}wifi_enabled\0\u{4}\u{2}wifi_ssid\0\u{3}wifi_psk\0\u{3}ntp_server\0\u{3}eth_enabled\0\u{3}address_mode\0\u{3}ipv4_config\0\u{3}rsyslog_server\0\u{3}enabled_protocols\0\u{3}ipv6_enabled\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2668,27 +2482,16 @@ extension Config.NetworkConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp } extension Config.NetworkConfig.AddressMode: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "DHCP"), - 1: .same(proto: "STATIC"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0DHCP\0\u{1}STATIC\0") } extension Config.NetworkConfig.ProtocolFlags: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "NO_BROADCAST"), - 1: .same(proto: "UDP_BROADCAST"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0NO_BROADCAST\0\u{1}UDP_BROADCAST\0") } 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 = [ - 1: .same(proto: "ip"), - 2: .same(proto: "gateway"), - 3: .same(proto: "subnet"), - 4: .same(proto: "dns"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}ip\0\u{1}gateway\0\u{1}subnet\0\u{1}dns\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2733,21 +2536,7 @@ extension Config.NetworkConfig.IpV4Config: SwiftProtobuf.Message, SwiftProtobuf. extension Config.DisplayConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = Config.protoMessageName + ".DisplayConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "screen_on_secs"), - 2: .standard(proto: "gps_format"), - 3: .standard(proto: "auto_screen_carousel_secs"), - 4: .standard(proto: "compass_north_top"), - 5: .standard(proto: "flip_screen"), - 6: .same(proto: "units"), - 7: .same(proto: "oled"), - 8: .same(proto: "displaymode"), - 9: .standard(proto: "heading_bold"), - 10: .standard(proto: "wake_on_tap_or_motion"), - 11: .standard(proto: "compass_orientation"), - 12: .standard(proto: "use_12h_clock"), - 13: .standard(proto: "use_long_node_name"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}screen_on_secs\0\u{3}gps_format\0\u{3}auto_screen_carousel_secs\0\u{3}compass_north_top\0\u{3}flip_screen\0\u{1}units\0\u{1}oled\0\u{1}displaymode\0\u{3}heading_bold\0\u{3}wake_on_tap_or_motion\0\u{3}compass_orientation\0\u{3}use_12h_clock\0\u{3}use_long_node_name\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2836,72 +2625,28 @@ extension Config.DisplayConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp } extension Config.DisplayConfig.DeprecatedGpsCoordinateFormat: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "UNUSED"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0UNUSED\0") } extension Config.DisplayConfig.DisplayUnits: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "METRIC"), - 1: .same(proto: "IMPERIAL"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0METRIC\0\u{1}IMPERIAL\0") } extension Config.DisplayConfig.OledType: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "OLED_AUTO"), - 1: .same(proto: "OLED_SSD1306"), - 2: .same(proto: "OLED_SH1106"), - 3: .same(proto: "OLED_SH1107"), - 4: .same(proto: "OLED_SH1107_128_128"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0OLED_AUTO\0\u{1}OLED_SSD1306\0\u{1}OLED_SH1106\0\u{1}OLED_SH1107\0\u{1}OLED_SH1107_128_128\0") } extension Config.DisplayConfig.DisplayMode: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "DEFAULT"), - 1: .same(proto: "TWOCOLOR"), - 2: .same(proto: "INVERTED"), - 3: .same(proto: "COLOR"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0DEFAULT\0\u{1}TWOCOLOR\0\u{1}INVERTED\0\u{1}COLOR\0") } extension Config.DisplayConfig.CompassOrientation: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "DEGREES_0"), - 1: .same(proto: "DEGREES_90"), - 2: .same(proto: "DEGREES_180"), - 3: .same(proto: "DEGREES_270"), - 4: .same(proto: "DEGREES_0_INVERTED"), - 5: .same(proto: "DEGREES_90_INVERTED"), - 6: .same(proto: "DEGREES_180_INVERTED"), - 7: .same(proto: "DEGREES_270_INVERTED"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0DEGREES_0\0\u{1}DEGREES_90\0\u{1}DEGREES_180\0\u{1}DEGREES_270\0\u{1}DEGREES_0_INVERTED\0\u{1}DEGREES_90_INVERTED\0\u{1}DEGREES_180_INVERTED\0\u{1}DEGREES_270_INVERTED\0") } extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = Config.protoMessageName + ".LoRaConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "use_preset"), - 2: .standard(proto: "modem_preset"), - 3: .same(proto: "bandwidth"), - 4: .standard(proto: "spread_factor"), - 5: .standard(proto: "coding_rate"), - 6: .standard(proto: "frequency_offset"), - 7: .same(proto: "region"), - 8: .standard(proto: "hop_limit"), - 9: .standard(proto: "tx_enabled"), - 10: .standard(proto: "tx_power"), - 11: .standard(proto: "channel_num"), - 12: .standard(proto: "override_duty_cycle"), - 13: .standard(proto: "sx126x_rx_boosted_gain"), - 14: .standard(proto: "override_frequency"), - 15: .standard(proto: "pa_fan_disabled"), - 103: .standard(proto: "ignore_incoming"), - 104: .standard(proto: "ignore_mqtt"), - 105: .standard(proto: "config_ok_to_mqtt"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}use_preset\0\u{3}modem_preset\0\u{1}bandwidth\0\u{3}spread_factor\0\u{3}coding_rate\0\u{3}frequency_offset\0\u{1}region\0\u{3}hop_limit\0\u{3}tx_enabled\0\u{3}tx_power\0\u{3}channel_num\0\u{3}override_duty_cycle\0\u{3}sx126x_rx_boosted_gain\0\u{3}override_frequency\0\u{3}pa_fan_disabled\0\u{4}X\u{1}ignore_incoming\0\u{3}ignore_mqtt\0\u{3}config_ok_to_mqtt\0") fileprivate class _StorageClass { var _usePreset: Bool = false @@ -2923,15 +2668,11 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem var _ignoreMqtt: Bool = false var _configOkToMqtt: Bool = false - #if swift(>=5.10) // This property is used as the initial default value for new instances of the type. // The type itself is protecting the reference to its storage via CoW semantics. // This will force a copy to be made of this reference when the first mutation occurs; // hence, it is safe to mark this as `nonisolated(unsafe)`. static nonisolated(unsafe) let defaultInstance = _StorageClass() - #else - static let defaultInstance = _StorageClass() - #endif private init() {} @@ -3013,7 +2754,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem if _storage._codingRate != 0 { try visitor.visitSingularUInt32Field(value: _storage._codingRate, fieldNumber: 5) } - if _storage._frequencyOffset != 0 { + if _storage._frequencyOffset.bitPattern != 0 { try visitor.visitSingularFloatField(value: _storage._frequencyOffset, fieldNumber: 6) } if _storage._region != .unset { @@ -3037,7 +2778,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem if _storage._sx126XRxBoostedGain != false { try visitor.visitSingularBoolField(value: _storage._sx126XRxBoostedGain, fieldNumber: 13) } - if _storage._overrideFrequency != 0 { + if _storage._overrideFrequency.bitPattern != 0 { try visitor.visitSingularFloatField(value: _storage._overrideFrequency, fieldNumber: 14) } if _storage._paFanDisabled != false { @@ -3089,58 +2830,16 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem } extension Config.LoRaConfig.RegionCode: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "UNSET"), - 1: .same(proto: "US"), - 2: .same(proto: "EU_433"), - 3: .same(proto: "EU_868"), - 4: .same(proto: "CN"), - 5: .same(proto: "JP"), - 6: .same(proto: "ANZ"), - 7: .same(proto: "KR"), - 8: .same(proto: "TW"), - 9: .same(proto: "RU"), - 10: .same(proto: "IN"), - 11: .same(proto: "NZ_865"), - 12: .same(proto: "TH"), - 13: .same(proto: "LORA_24"), - 14: .same(proto: "UA_433"), - 15: .same(proto: "UA_868"), - 16: .same(proto: "MY_433"), - 17: .same(proto: "MY_919"), - 18: .same(proto: "SG_923"), - 19: .same(proto: "PH_433"), - 20: .same(proto: "PH_868"), - 21: .same(proto: "PH_915"), - 22: .same(proto: "ANZ_433"), - 23: .same(proto: "KZ_433"), - 24: .same(proto: "KZ_863"), - 25: .same(proto: "NP_865"), - 26: .same(proto: "BR_902"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0UNSET\0\u{1}US\0\u{1}EU_433\0\u{1}EU_868\0\u{1}CN\0\u{1}JP\0\u{1}ANZ\0\u{1}KR\0\u{1}TW\0\u{1}RU\0\u{1}IN\0\u{1}NZ_865\0\u{1}TH\0\u{1}LORA_24\0\u{1}UA_433\0\u{1}UA_868\0\u{1}MY_433\0\u{1}MY_919\0\u{1}SG_923\0\u{1}PH_433\0\u{1}PH_868\0\u{1}PH_915\0\u{1}ANZ_433\0\u{1}KZ_433\0\u{1}KZ_863\0\u{1}NP_865\0\u{1}BR_902\0") } extension Config.LoRaConfig.ModemPreset: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "LONG_FAST"), - 1: .same(proto: "LONG_SLOW"), - 2: .same(proto: "VERY_LONG_SLOW"), - 3: .same(proto: "MEDIUM_SLOW"), - 4: .same(proto: "MEDIUM_FAST"), - 5: .same(proto: "SHORT_SLOW"), - 6: .same(proto: "SHORT_FAST"), - 7: .same(proto: "LONG_MODERATE"), - 8: .same(proto: "SHORT_TURBO"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0LONG_FAST\0\u{1}LONG_SLOW\0\u{1}VERY_LONG_SLOW\0\u{1}MEDIUM_SLOW\0\u{1}MEDIUM_FAST\0\u{1}SHORT_SLOW\0\u{1}SHORT_FAST\0\u{1}LONG_MODERATE\0\u{1}SHORT_TURBO\0\u{1}LONG_TURBO\0") } extension Config.BluetoothConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = Config.protoMessageName + ".BluetoothConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "enabled"), - 2: .same(proto: "mode"), - 3: .standard(proto: "fixed_pin"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}enabled\0\u{1}mode\0\u{3}fixed_pin\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -3179,24 +2878,12 @@ extension Config.BluetoothConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageI } extension Config.BluetoothConfig.PairingMode: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "RANDOM_PIN"), - 1: .same(proto: "FIXED_PIN"), - 2: .same(proto: "NO_PIN"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0RANDOM_PIN\0\u{1}FIXED_PIN\0\u{1}NO_PIN\0") } extension Config.SecurityConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = Config.protoMessageName + ".SecurityConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "public_key"), - 2: .standard(proto: "private_key"), - 3: .standard(proto: "admin_key"), - 4: .standard(proto: "is_managed"), - 5: .standard(proto: "serial_enabled"), - 6: .standard(proto: "debug_log_api_enabled"), - 8: .standard(proto: "admin_channel_enabled"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}public_key\0\u{3}private_key\0\u{3}admin_key\0\u{3}is_managed\0\u{3}serial_enabled\0\u{3}debug_log_api_enabled\0\u{4}\u{2}admin_channel_enabled\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -3259,8 +2946,8 @@ extension Config.SessionkeyConfig: SwiftProtobuf.Message, SwiftProtobuf._Message public static let _protobuf_nameMap = SwiftProtobuf._NameMap() public mutating func decodeMessage(decoder: inout D) throws { - while let _ = try decoder.nextFieldNumber() { - } + // Load everything into unknown fields + while try decoder.nextFieldNumber() != nil {} } public func traverse(visitor: inout V) throws { diff --git a/MeshtasticProtobufs/Sources/meshtastic/connection_status.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/connection_status.pb.swift index a2ec180e..d1f1d208 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/connection_status.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/connection_status.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/connection_status.proto @@ -7,7 +8,6 @@ // 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 @@ -20,7 +20,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP typealias Version = _2 } -public struct DeviceConnectionStatus { +public struct DeviceConnectionStatus: Sendable { // 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. @@ -81,7 +81,7 @@ public struct DeviceConnectionStatus { /// /// WiFi connection status -public struct WifiConnectionStatus { +public struct WifiConnectionStatus: Sendable { // 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. @@ -114,7 +114,7 @@ public struct WifiConnectionStatus { /// /// Ethernet connection status -public struct EthernetConnectionStatus { +public struct EthernetConnectionStatus: Sendable { // 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. @@ -139,7 +139,7 @@ public struct EthernetConnectionStatus { /// /// Ethernet or WiFi connection status -public struct NetworkConnectionStatus { +public struct NetworkConnectionStatus: Sendable { // 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. @@ -167,7 +167,7 @@ public struct NetworkConnectionStatus { /// /// Bluetooth connection status -public struct BluetoothConnectionStatus { +public struct BluetoothConnectionStatus: Sendable { // 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. @@ -191,7 +191,7 @@ public struct BluetoothConnectionStatus { /// /// Serial connection status -public struct SerialConnectionStatus { +public struct SerialConnectionStatus: Sendable { // 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. @@ -209,27 +209,13 @@ public struct SerialConnectionStatus { public init() {} } -#if swift(>=5.5) && canImport(_Concurrency) -extension DeviceConnectionStatus: @unchecked Sendable {} -extension WifiConnectionStatus: @unchecked Sendable {} -extension EthernetConnectionStatus: @unchecked Sendable {} -extension NetworkConnectionStatus: @unchecked Sendable {} -extension BluetoothConnectionStatus: @unchecked Sendable {} -extension SerialConnectionStatus: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" extension DeviceConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".DeviceConnectionStatus" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "wifi"), - 2: .same(proto: "ethernet"), - 3: .same(proto: "bluetooth"), - 4: .same(proto: "serial"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}wifi\0\u{1}ethernet\0\u{1}bluetooth\0\u{1}serial\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -278,11 +264,7 @@ extension DeviceConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageI extension WifiConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".WifiConnectionStatus" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "status"), - 2: .same(proto: "ssid"), - 3: .same(proto: "rssi"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}status\0\u{1}ssid\0\u{1}rssi\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -326,9 +308,7 @@ extension WifiConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageImp extension EthernetConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".EthernetConnectionStatus" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "status"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}status\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -362,12 +342,7 @@ extension EthernetConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._Messag extension NetworkConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".NetworkConnectionStatus" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "ip_address"), - 2: .standard(proto: "is_connected"), - 3: .standard(proto: "is_mqtt_connected"), - 4: .standard(proto: "is_syslog_connected"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}ip_address\0\u{3}is_connected\0\u{3}is_mqtt_connected\0\u{3}is_syslog_connected\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -412,11 +387,7 @@ extension NetworkConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._Message extension BluetoothConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".BluetoothConnectionStatus" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "pin"), - 2: .same(proto: "rssi"), - 3: .standard(proto: "is_connected"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}pin\0\u{1}rssi\0\u{3}is_connected\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -456,10 +427,7 @@ extension BluetoothConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._Messa extension SerialConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".SerialConnectionStatus" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "baud"), - 2: .standard(proto: "is_connected"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}baud\0\u{3}is_connected\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { diff --git a/MeshtasticProtobufs/Sources/meshtastic/device_ui.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/device_ui.pb.swift index 4f049e79..327e356d 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/device_ui.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/device_ui.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/device_ui.proto @@ -20,7 +21,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP typealias Version = _2 } -public enum CompassMode: SwiftProtobuf.Enum { +public enum CompassMode: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -58,22 +59,16 @@ public enum CompassMode: SwiftProtobuf.Enum { } } -} - -#if swift(>=4.2) - -extension CompassMode: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [CompassMode] = [ .dynamic, .fixedRing, .freezeHeading, ] + } -#endif // swift(>=4.2) - -public enum Theme: SwiftProtobuf.Enum { +public enum Theme: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -111,24 +106,18 @@ public enum Theme: SwiftProtobuf.Enum { } } -} - -#if swift(>=4.2) - -extension Theme: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [Theme] = [ .dark, .light, .red, ] -} -#endif // swift(>=4.2) +} /// /// Localization -public enum Language: SwiftProtobuf.Enum { +public enum Language: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -280,11 +269,6 @@ public enum Language: SwiftProtobuf.Enum { } } -} - -#if swift(>=4.2) - -extension Language: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [Language] = [ .english, @@ -310,11 +294,10 @@ extension Language: CaseIterable { .simplifiedChinese, .traditionalChinese, ] + } -#endif // swift(>=4.2) - -public struct DeviceUIConfig { +public struct DeviceUIConfig: @unchecked Sendable { // 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. @@ -462,7 +445,7 @@ public struct DeviceUIConfig { /// /// How the GPS coordinates are displayed on the OLED screen. - public enum GpsCoordinateFormat: SwiftProtobuf.Enum { + public enum GpsCoordinateFormat: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -532,6 +515,17 @@ public struct DeviceUIConfig { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [DeviceUIConfig.GpsCoordinateFormat] = [ + .dec, + .dms, + .utm, + .mgrs, + .olc, + .osgr, + .mls, + ] + } public init() {} @@ -539,24 +533,7 @@ public struct DeviceUIConfig { fileprivate var _storage = _StorageClass.defaultInstance } -#if swift(>=4.2) - -extension DeviceUIConfig.GpsCoordinateFormat: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [DeviceUIConfig.GpsCoordinateFormat] = [ - .dec, - .dms, - .utm, - .mgrs, - .olc, - .osgr, - .mls, - ] -} - -#endif // swift(>=4.2) - -public struct NodeFilter { +public struct NodeFilter: Sendable { // 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. @@ -594,7 +571,7 @@ public struct NodeFilter { public init() {} } -public struct NodeHighlight { +public struct NodeHighlight: Sendable { // 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. @@ -624,7 +601,7 @@ public struct NodeHighlight { public init() {} } -public struct GeoPoint { +public struct GeoPoint: Sendable { // 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. @@ -646,7 +623,7 @@ public struct GeoPoint { public init() {} } -public struct Map { +public struct Map: Sendable { // 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. @@ -677,88 +654,25 @@ public struct Map { fileprivate var _home: GeoPoint? = nil } -#if swift(>=5.5) && canImport(_Concurrency) -extension CompassMode: @unchecked Sendable {} -extension Theme: @unchecked Sendable {} -extension Language: @unchecked Sendable {} -extension DeviceUIConfig: @unchecked Sendable {} -extension DeviceUIConfig.GpsCoordinateFormat: @unchecked Sendable {} -extension NodeFilter: @unchecked Sendable {} -extension NodeHighlight: @unchecked Sendable {} -extension GeoPoint: @unchecked Sendable {} -extension Map: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" extension CompassMode: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "DYNAMIC"), - 1: .same(proto: "FIXED_RING"), - 2: .same(proto: "FREEZE_HEADING"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0DYNAMIC\0\u{1}FIXED_RING\0\u{1}FREEZE_HEADING\0") } extension Theme: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "DARK"), - 1: .same(proto: "LIGHT"), - 2: .same(proto: "RED"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0DARK\0\u{1}LIGHT\0\u{1}RED\0") } extension Language: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "ENGLISH"), - 1: .same(proto: "FRENCH"), - 2: .same(proto: "GERMAN"), - 3: .same(proto: "ITALIAN"), - 4: .same(proto: "PORTUGUESE"), - 5: .same(proto: "SPANISH"), - 6: .same(proto: "SWEDISH"), - 7: .same(proto: "FINNISH"), - 8: .same(proto: "POLISH"), - 9: .same(proto: "TURKISH"), - 10: .same(proto: "SERBIAN"), - 11: .same(proto: "RUSSIAN"), - 12: .same(proto: "DUTCH"), - 13: .same(proto: "GREEK"), - 14: .same(proto: "NORWEGIAN"), - 15: .same(proto: "SLOVENIAN"), - 16: .same(proto: "UKRAINIAN"), - 17: .same(proto: "BULGARIAN"), - 18: .same(proto: "CZECH"), - 19: .same(proto: "DANISH"), - 30: .same(proto: "SIMPLIFIED_CHINESE"), - 31: .same(proto: "TRADITIONAL_CHINESE"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0ENGLISH\0\u{1}FRENCH\0\u{1}GERMAN\0\u{1}ITALIAN\0\u{1}PORTUGUESE\0\u{1}SPANISH\0\u{1}SWEDISH\0\u{1}FINNISH\0\u{1}POLISH\0\u{1}TURKISH\0\u{1}SERBIAN\0\u{1}RUSSIAN\0\u{1}DUTCH\0\u{1}GREEK\0\u{1}NORWEGIAN\0\u{1}SLOVENIAN\0\u{1}UKRAINIAN\0\u{1}BULGARIAN\0\u{1}CZECH\0\u{1}DANISH\0\u{2}\u{b}SIMPLIFIED_CHINESE\0\u{1}TRADITIONAL_CHINESE\0") } extension DeviceUIConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".DeviceUIConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "version"), - 2: .standard(proto: "screen_brightness"), - 3: .standard(proto: "screen_timeout"), - 4: .standard(proto: "screen_lock"), - 5: .standard(proto: "settings_lock"), - 6: .standard(proto: "pin_code"), - 7: .same(proto: "theme"), - 8: .standard(proto: "alert_enabled"), - 9: .standard(proto: "banner_enabled"), - 10: .standard(proto: "ring_tone_id"), - 11: .same(proto: "language"), - 12: .standard(proto: "node_filter"), - 13: .standard(proto: "node_highlight"), - 14: .standard(proto: "calibration_data"), - 15: .standard(proto: "map_data"), - 16: .standard(proto: "compass_mode"), - 17: .standard(proto: "screen_rgb_color"), - 18: .standard(proto: "is_clockface_analog"), - 19: .standard(proto: "gps_format"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}version\0\u{3}screen_brightness\0\u{3}screen_timeout\0\u{3}screen_lock\0\u{3}settings_lock\0\u{3}pin_code\0\u{1}theme\0\u{3}alert_enabled\0\u{3}banner_enabled\0\u{3}ring_tone_id\0\u{1}language\0\u{3}node_filter\0\u{3}node_highlight\0\u{3}calibration_data\0\u{3}map_data\0\u{3}compass_mode\0\u{3}screen_rgb_color\0\u{3}is_clockface_analog\0\u{3}gps_format\0") fileprivate class _StorageClass { var _version: UInt32 = 0 @@ -781,15 +695,11 @@ extension DeviceUIConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement var _isClockfaceAnalog: Bool = false var _gpsFormat: DeviceUIConfig.GpsCoordinateFormat = .dec - #if swift(>=5.10) // This property is used as the initial default value for new instances of the type. // The type itself is protecting the reference to its storage via CoW semantics. // This will force a copy to be made of this reference when the first mutation occurs; // hence, it is safe to mark this as `nonisolated(unsafe)`. static nonisolated(unsafe) let defaultInstance = _StorageClass() - #else - static let defaultInstance = _StorageClass() - #endif private init() {} @@ -957,28 +867,12 @@ extension DeviceUIConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement } extension DeviceUIConfig.GpsCoordinateFormat: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "DEC"), - 1: .same(proto: "DMS"), - 2: .same(proto: "UTM"), - 3: .same(proto: "MGRS"), - 4: .same(proto: "OLC"), - 5: .same(proto: "OSGR"), - 6: .same(proto: "MLS"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0DEC\0\u{1}DMS\0\u{1}UTM\0\u{1}MGRS\0\u{1}OLC\0\u{1}OSGR\0\u{1}MLS\0") } extension NodeFilter: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".NodeFilter" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "unknown_switch"), - 2: .standard(proto: "offline_switch"), - 3: .standard(proto: "public_key_switch"), - 4: .standard(proto: "hops_away"), - 5: .standard(proto: "position_switch"), - 6: .standard(proto: "node_name"), - 7: .same(proto: "channel"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}unknown_switch\0\u{3}offline_switch\0\u{3}public_key_switch\0\u{3}hops_away\0\u{3}position_switch\0\u{3}node_name\0\u{1}channel\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -1038,13 +932,7 @@ extension NodeFilter: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio extension NodeHighlight: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".NodeHighlight" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "chat_switch"), - 2: .standard(proto: "position_switch"), - 3: .standard(proto: "telemetry_switch"), - 4: .standard(proto: "iaq_switch"), - 5: .standard(proto: "node_name"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}chat_switch\0\u{3}position_switch\0\u{3}telemetry_switch\0\u{3}iaq_switch\0\u{3}node_name\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -1094,11 +982,7 @@ extension NodeHighlight: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa extension GeoPoint: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".GeoPoint" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "zoom"), - 2: .same(proto: "latitude"), - 3: .same(proto: "longitude"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}zoom\0\u{1}latitude\0\u{1}longitude\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -1138,11 +1022,7 @@ extension GeoPoint: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB extension Map: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Map" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "home"), - 2: .same(proto: "style"), - 3: .standard(proto: "follow_gps"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}home\0\u{1}style\0\u{3}follow_gps\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { diff --git a/MeshtasticProtobufs/Sources/meshtastic/deviceonly.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/deviceonly.pb.swift index 9c8d5ad8..281f18d8 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/deviceonly.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/deviceonly.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/deviceonly.proto @@ -22,7 +23,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// /// Position with static location information only for NodeDBLite -public struct PositionLite { +public struct PositionLite: Sendable { // 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. @@ -57,13 +58,15 @@ public struct PositionLite { public init() {} } -public struct UserLite { +public struct UserLite: Sendable { // 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. /// /// This is the addr of the radio. + /// + /// NOTE: This field was marked as deprecated in the .proto file. public var macaddr: Data = Data() /// @@ -115,7 +118,7 @@ public struct UserLite { fileprivate var _isUnmessagable: Bool? = nil } -public struct NodeInfoLite { +public struct NodeInfoLite: @unchecked Sendable { // 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. @@ -245,7 +248,7 @@ public struct NodeInfoLite { /// FIXME, since we write this each time we enter deep sleep (and have infinite /// flash) it would be better to use some sort of append only data structure for /// the receive queue and use the preferences store for the other stuff -public struct DeviceState { +public struct DeviceState: @unchecked Sendable { // 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. @@ -305,6 +308,8 @@ public struct DeviceState { /// Used only during development. /// Indicates developer is testing and changes should never be saved to flash. /// Deprecated in 2.3.1 + /// + /// NOTE: This field was marked as deprecated in the .proto file. public var noSave: Bool { get {return _storage._noSave} set {_uniqueStorage()._noSave = newValue} @@ -313,6 +318,8 @@ public struct DeviceState { /// /// Previously used to manage GPS factory resets. /// Deprecated in 2.5.23 + /// + /// NOTE: This field was marked as deprecated in the .proto file. public var didGpsReset: Bool { get {return _storage._didGpsReset} set {_uniqueStorage()._didGpsReset = newValue} @@ -345,7 +352,7 @@ public struct DeviceState { fileprivate var _storage = _StorageClass.defaultInstance } -public struct NodeDatabase { +public struct NodeDatabase: Sendable { // 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. @@ -367,7 +374,7 @@ public struct NodeDatabase { /// /// The on-disk saved channels -public struct ChannelFile { +public struct ChannelFile: Sendable { // 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. @@ -389,7 +396,7 @@ public struct ChannelFile { /// /// The on-disk backup of the node's preferences -public struct BackupPreferences { +public struct BackupPreferences: Sendable { // 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. @@ -456,29 +463,13 @@ public struct BackupPreferences { fileprivate var _owner: User? = nil } -#if swift(>=5.5) && canImport(_Concurrency) -extension PositionLite: @unchecked Sendable {} -extension UserLite: @unchecked Sendable {} -extension NodeInfoLite: @unchecked Sendable {} -extension DeviceState: @unchecked Sendable {} -extension NodeDatabase: @unchecked Sendable {} -extension ChannelFile: @unchecked Sendable {} -extension BackupPreferences: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" extension PositionLite: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".PositionLite" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "latitude_i"), - 2: .standard(proto: "longitude_i"), - 3: .same(proto: "altitude"), - 4: .same(proto: "time"), - 5: .standard(proto: "location_source"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}latitude_i\0\u{3}longitude_i\0\u{1}altitude\0\u{1}time\0\u{3}location_source\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -528,16 +519,7 @@ extension PositionLite: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat extension UserLite: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".UserLite" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "macaddr"), - 2: .standard(proto: "long_name"), - 3: .standard(proto: "short_name"), - 4: .standard(proto: "hw_model"), - 5: .standard(proto: "is_licensed"), - 6: .same(proto: "role"), - 7: .standard(proto: "public_key"), - 9: .standard(proto: "is_unmessagable"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}macaddr\0\u{3}long_name\0\u{3}short_name\0\u{3}hw_model\0\u{3}is_licensed\0\u{1}role\0\u{3}public_key\0\u{4}\u{2}is_unmessagable\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -606,21 +588,7 @@ extension UserLite: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB extension NodeInfoLite: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".NodeInfoLite" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "num"), - 2: .same(proto: "user"), - 3: .same(proto: "position"), - 4: .same(proto: "snr"), - 5: .standard(proto: "last_heard"), - 6: .standard(proto: "device_metrics"), - 7: .same(proto: "channel"), - 8: .standard(proto: "via_mqtt"), - 9: .standard(proto: "hops_away"), - 10: .standard(proto: "is_favorite"), - 11: .standard(proto: "is_ignored"), - 12: .standard(proto: "next_hop"), - 13: .same(proto: "bitfield"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}num\0\u{1}user\0\u{1}position\0\u{1}snr\0\u{3}last_heard\0\u{3}device_metrics\0\u{1}channel\0\u{3}via_mqtt\0\u{3}hops_away\0\u{3}is_favorite\0\u{3}is_ignored\0\u{3}next_hop\0\u{1}bitfield\0") fileprivate class _StorageClass { var _num: UInt32 = 0 @@ -637,15 +605,11 @@ extension NodeInfoLite: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat var _nextHop: UInt32 = 0 var _bitfield: UInt32 = 0 - #if swift(>=5.10) // This property is used as the initial default value for new instances of the type. // The type itself is protecting the reference to its storage via CoW semantics. // This will force a copy to be made of this reference when the first mutation occurs; // hence, it is safe to mark this as `nonisolated(unsafe)`. static nonisolated(unsafe) let defaultInstance = _StorageClass() - #else - static let defaultInstance = _StorageClass() - #endif private init() {} @@ -715,7 +679,7 @@ extension NodeInfoLite: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat try { if let v = _storage._position { try visitor.visitSingularMessageField(value: v, fieldNumber: 3) } }() - if _storage._snr != 0 { + if _storage._snr.bitPattern != 0 { try visitor.visitSingularFloatField(value: _storage._snr, fieldNumber: 4) } if _storage._lastHeard != 0 { @@ -778,17 +742,7 @@ extension NodeInfoLite: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".DeviceState" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 2: .standard(proto: "my_node"), - 3: .same(proto: "owner"), - 5: .standard(proto: "receive_queue"), - 8: .same(proto: "version"), - 7: .standard(proto: "rx_text_message"), - 9: .standard(proto: "no_save"), - 11: .standard(proto: "did_gps_reset"), - 12: .standard(proto: "rx_waypoint"), - 13: .standard(proto: "node_remote_hardware_pins"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{4}\u{2}my_node\0\u{1}owner\0\u{4}\u{2}receive_queue\0\u{4}\u{2}rx_text_message\0\u{1}version\0\u{3}no_save\0\u{4}\u{2}did_gps_reset\0\u{3}rx_waypoint\0\u{3}node_remote_hardware_pins\0") fileprivate class _StorageClass { var _myNode: MyNodeInfo? = nil @@ -801,15 +755,11 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati var _rxWaypoint: MeshPacket? = nil var _nodeRemoteHardwarePins: [NodeRemoteHardwarePin] = [] - #if swift(>=5.10) // This property is used as the initial default value for new instances of the type. // The type itself is protecting the reference to its storage via CoW semantics. // This will force a copy to be made of this reference when the first mutation occurs; // hence, it is safe to mark this as `nonisolated(unsafe)`. static nonisolated(unsafe) let defaultInstance = _StorageClass() - #else - static let defaultInstance = _StorageClass() - #endif private init() {} @@ -918,10 +868,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati extension NodeDatabase: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".NodeDatabase" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "version"), - 2: .same(proto: "nodes"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}version\0\u{1}nodes\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -956,10 +903,7 @@ extension NodeDatabase: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat extension ChannelFile: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".ChannelFile" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "channels"), - 2: .same(proto: "version"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}channels\0\u{1}version\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -994,14 +938,7 @@ extension ChannelFile: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati extension BackupPreferences: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".BackupPreferences" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "version"), - 2: .same(proto: "timestamp"), - 3: .same(proto: "config"), - 4: .standard(proto: "module_config"), - 5: .same(proto: "channels"), - 6: .same(proto: "owner"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}version\0\u{1}timestamp\0\u{1}config\0\u{3}module_config\0\u{1}channels\0\u{1}owner\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { diff --git a/MeshtasticProtobufs/Sources/meshtastic/interdevice.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/interdevice.pb.swift index 92b72c15..3e28112d 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/interdevice.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/interdevice.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/interdevice.proto @@ -7,7 +8,6 @@ // 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 @@ -20,7 +20,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP typealias Version = _2 } -public enum MessageType: SwiftProtobuf.Enum { +public enum MessageType: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int case ack // = 0 @@ -82,11 +82,6 @@ public enum MessageType: SwiftProtobuf.Enum { } } -} - -#if swift(>=4.2) - -extension MessageType: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [MessageType] = [ .ack, @@ -102,11 +97,10 @@ extension MessageType: CaseIterable { .aht20Humidity, .tvocIndex, ] + } -#endif // swift(>=4.2) - -public struct SensorData { +public struct SensorData: Sendable { // 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. @@ -136,34 +130,16 @@ public struct SensorData { public var unknownFields = SwiftProtobuf.UnknownStorage() /// The sensor data, either as a float or an uint32 - public enum OneOf_Data: Equatable { + public enum OneOf_Data: Equatable, Sendable { case floatValue(Float) case uint32Value(UInt32) - #if !swift(>=4.1) - public static func ==(lhs: SensorData.OneOf_Data, rhs: SensorData.OneOf_Data) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.floatValue, .floatValue): return { - guard case .floatValue(let l) = lhs, case .floatValue(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.uint32Value, .uint32Value): return { - guard case .uint32Value(let l) = lhs, case .uint32Value(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } public init() {} } -public struct InterdeviceMessage { +public struct InterdeviceMessage: Sendable { // 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. @@ -190,69 +166,26 @@ public struct InterdeviceMessage { public var unknownFields = SwiftProtobuf.UnknownStorage() /// The message data - public enum OneOf_Data: Equatable { + public enum OneOf_Data: Equatable, Sendable { case nmea(String) case sensor(SensorData) - #if !swift(>=4.1) - public static func ==(lhs: InterdeviceMessage.OneOf_Data, rhs: InterdeviceMessage.OneOf_Data) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.nmea, .nmea): return { - guard case .nmea(let l) = lhs, case .nmea(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.sensor, .sensor): return { - guard case .sensor(let l) = lhs, case .sensor(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } public init() {} } -#if swift(>=5.5) && canImport(_Concurrency) -extension MessageType: @unchecked Sendable {} -extension SensorData: @unchecked Sendable {} -extension SensorData.OneOf_Data: @unchecked Sendable {} -extension InterdeviceMessage: @unchecked Sendable {} -extension InterdeviceMessage.OneOf_Data: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" extension MessageType: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "ACK"), - 160: .same(proto: "COLLECT_INTERVAL"), - 161: .same(proto: "BEEP_ON"), - 162: .same(proto: "BEEP_OFF"), - 163: .same(proto: "SHUTDOWN"), - 164: .same(proto: "POWER_ON"), - 176: .same(proto: "SCD41_TEMP"), - 177: .same(proto: "SCD41_HUMIDITY"), - 178: .same(proto: "SCD41_CO2"), - 179: .same(proto: "AHT20_TEMP"), - 180: .same(proto: "AHT20_HUMIDITY"), - 181: .same(proto: "TVOC_INDEX"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0ACK\0\u{2}`\u{2}COLLECT_INTERVAL\0\u{1}BEEP_ON\0\u{1}BEEP_OFF\0\u{1}SHUTDOWN\0\u{1}POWER_ON\0\u{2}\u{c}SCD41_TEMP\0\u{1}SCD41_HUMIDITY\0\u{1}SCD41_CO2\0\u{1}AHT20_TEMP\0\u{1}AHT20_HUMIDITY\0\u{1}TVOC_INDEX\0") } extension SensorData: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".SensorData" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "type"), - 2: .standard(proto: "float_value"), - 3: .standard(proto: "uint32_value"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}type\0\u{3}float_value\0\u{3}uint32_value\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -314,10 +247,7 @@ extension SensorData: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio extension InterdeviceMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".InterdeviceMessage" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "nmea"), - 2: .same(proto: "sensor"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}nmea\0\u{1}sensor\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { diff --git a/MeshtasticProtobufs/Sources/meshtastic/localonly.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/localonly.pb.swift index 0af27466..9ba9dd88 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/localonly.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/localonly.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/localonly.proto @@ -7,7 +8,6 @@ // 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 @@ -20,7 +20,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP typealias Version = _2 } -public struct LocalConfig { +public struct LocalConfig: @unchecked Sendable { // 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. @@ -129,7 +129,7 @@ public struct LocalConfig { fileprivate var _storage = _StorageClass.defaultInstance } -public struct LocalModuleConfig { +public struct LocalModuleConfig: @unchecked Sendable { // 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. @@ -293,28 +293,13 @@ public struct LocalModuleConfig { fileprivate var _storage = _StorageClass.defaultInstance } -#if swift(>=5.5) && canImport(_Concurrency) -extension LocalConfig: @unchecked Sendable {} -extension LocalModuleConfig: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" extension LocalConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".LocalConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "device"), - 2: .same(proto: "position"), - 3: .same(proto: "power"), - 4: .same(proto: "network"), - 5: .same(proto: "display"), - 6: .same(proto: "lora"), - 7: .same(proto: "bluetooth"), - 8: .same(proto: "version"), - 9: .same(proto: "security"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}device\0\u{1}position\0\u{1}power\0\u{1}network\0\u{1}display\0\u{1}lora\0\u{1}bluetooth\0\u{1}version\0\u{1}security\0") fileprivate class _StorageClass { var _device: Config.DeviceConfig? = nil @@ -327,15 +312,11 @@ extension LocalConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati var _version: UInt32 = 0 var _security: Config.SecurityConfig? = nil - #if swift(>=5.10) // This property is used as the initial default value for new instances of the type. // The type itself is protecting the reference to its storage via CoW semantics. // This will force a copy to be made of this reference when the first mutation occurs; // hence, it is safe to mark this as `nonisolated(unsafe)`. static nonisolated(unsafe) let defaultInstance = _StorageClass() - #else - static let defaultInstance = _StorageClass() - #endif private init() {} @@ -444,22 +425,7 @@ extension LocalConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati extension LocalModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".LocalModuleConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "mqtt"), - 2: .same(proto: "serial"), - 3: .standard(proto: "external_notification"), - 4: .standard(proto: "store_forward"), - 5: .standard(proto: "range_test"), - 6: .same(proto: "telemetry"), - 7: .standard(proto: "canned_message"), - 9: .same(proto: "audio"), - 10: .standard(proto: "remote_hardware"), - 11: .standard(proto: "neighbor_info"), - 12: .standard(proto: "ambient_lighting"), - 13: .standard(proto: "detection_sensor"), - 14: .same(proto: "paxcounter"), - 8: .same(proto: "version"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}mqtt\0\u{1}serial\0\u{3}external_notification\0\u{3}store_forward\0\u{3}range_test\0\u{1}telemetry\0\u{3}canned_message\0\u{1}version\0\u{1}audio\0\u{3}remote_hardware\0\u{3}neighbor_info\0\u{3}ambient_lighting\0\u{3}detection_sensor\0\u{1}paxcounter\0") fileprivate class _StorageClass { var _mqtt: ModuleConfig.MQTTConfig? = nil @@ -477,15 +443,11 @@ extension LocalModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem var _paxcounter: ModuleConfig.PaxcounterConfig? = nil var _version: UInt32 = 0 - #if swift(>=5.10) // This property is used as the initial default value for new instances of the type. // The type itself is protecting the reference to its storage via CoW semantics. // This will force a copy to be made of this reference when the first mutation occurs; // hence, it is safe to mark this as `nonisolated(unsafe)`. static nonisolated(unsafe) let defaultInstance = _StorageClass() - #else - static let defaultInstance = _StorageClass() - #endif private init() {} diff --git a/MeshtasticProtobufs/Sources/meshtastic/mesh.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/mesh.pb.swift index c32dfb49..477e2457 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/mesh.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/mesh.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/mesh.proto @@ -25,7 +26,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// bin/build-all.sh script. /// Because they will be used to find firmware filenames in the android app for OTA updates. /// To match the old style filenames, _ is converted to -, p is converted to . -public enum HardwareModel: SwiftProtobuf.Enum { +public enum HardwareModel: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -422,8 +423,8 @@ public enum HardwareModel: SwiftProtobuf.Enum { case heltecSensorHub // = 92 /// - /// Reserved Fried Chicken ID for future use - case reservedFriedChicken // = 93 + /// Muzi Works Muzi-Base device + case muziBase // = 93 /// /// Heltec Magnetic Power Bank with Meshtastic compatible @@ -510,6 +511,30 @@ public enum HardwareModel: SwiftProtobuf.Enum { /// LilyGo T-Watch Ultra case tWatchUltra // = 114 + /// + /// Elecrow ThinkNode M3 + case thinknodeM3 // = 115 + + /// + /// RAK WISMESH_TAP_V2 with ESP32-S3 CPU + case wismeshTapV2 // = 116 + + /// + /// RAK3401 + case rak3401 // = 117 + + /// + /// RAK6421 Hat+ + case rak6421 // = 118 + + /// + /// Elecrow ThinkNode M4 + case thinknodeM4 // = 119 + + /// + /// Elecrow ThinkNode M6 + case thinknodeM6 // = 120 + /// /// ------------------------------------------------------------------------------------------------------------------------------------------ /// Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. @@ -616,7 +641,7 @@ public enum HardwareModel: SwiftProtobuf.Enum { case 90: self = .thinknodeM2 case 91: self = .tEthElite case 92: self = .heltecSensorHub - case 93: self = .reservedFriedChicken + case 93: self = .muziBase case 94: self = .heltecMeshPocket case 95: self = .seeedSolarNode case 96: self = .nomadstarMeteorPro @@ -638,6 +663,12 @@ public enum HardwareModel: SwiftProtobuf.Enum { case 112: self = .m5StackCardputerAdv case 113: self = .heltecWirelessTrackerV2 case 114: self = .tWatchUltra + case 115: self = .thinknodeM3 + case 116: self = .wismeshTapV2 + case 117: self = .rak3401 + case 118: self = .rak6421 + case 119: self = .thinknodeM4 + case 120: self = .thinknodeM6 case 255: self = .privateHw default: self = .UNRECOGNIZED(rawValue) } @@ -738,7 +769,7 @@ public enum HardwareModel: SwiftProtobuf.Enum { case .thinknodeM2: return 90 case .tEthElite: return 91 case .heltecSensorHub: return 92 - case .reservedFriedChicken: return 93 + case .muziBase: return 93 case .heltecMeshPocket: return 94 case .seeedSolarNode: return 95 case .nomadstarMeteorPro: return 96 @@ -760,16 +791,17 @@ public enum HardwareModel: SwiftProtobuf.Enum { case .m5StackCardputerAdv: return 112 case .heltecWirelessTrackerV2: return 113 case .tWatchUltra: return 114 + case .thinknodeM3: return 115 + case .wismeshTapV2: return 116 + case .rak3401: return 117 + case .rak6421: return 118 + case .thinknodeM4: return 119 + case .thinknodeM6: return 120 case .privateHw: return 255 case .UNRECOGNIZED(let i): return i } } -} - -#if swift(>=4.2) - -extension HardwareModel: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [HardwareModel] = [ .unset, @@ -865,7 +897,7 @@ extension HardwareModel: CaseIterable { .thinknodeM2, .tEthElite, .heltecSensorHub, - .reservedFriedChicken, + .muziBase, .heltecMeshPocket, .seeedSolarNode, .nomadstarMeteorPro, @@ -887,15 +919,20 @@ extension HardwareModel: CaseIterable { .m5StackCardputerAdv, .heltecWirelessTrackerV2, .tWatchUltra, + .thinknodeM3, + .wismeshTapV2, + .rak3401, + .rak6421, + .thinknodeM4, + .thinknodeM6, .privateHw, ] -} -#endif // swift(>=4.2) +} /// /// Shared constants between device and phone -public enum Constants: SwiftProtobuf.Enum { +public enum Constants: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -930,26 +967,20 @@ public enum Constants: SwiftProtobuf.Enum { } } -} - -#if swift(>=4.2) - -extension Constants: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [Constants] = [ .zero, .dataPayloadLen, ] -} -#endif // swift(>=4.2) +} /// /// Error codes for critical errors /// The device might report these fault codes on the screen. /// If you encounter a fault code, please post on the meshtastic.discourse.group /// and we'll try to help. -public enum CriticalErrorCode: SwiftProtobuf.Enum { +public enum CriticalErrorCode: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1058,11 +1089,6 @@ public enum CriticalErrorCode: SwiftProtobuf.Enum { } } -} - -#if swift(>=4.2) - -extension CriticalErrorCode: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [CriticalErrorCode] = [ .none, @@ -1080,14 +1106,13 @@ extension CriticalErrorCode: CaseIterable { .flashCorruptionRecoverable, .flashCorruptionUnrecoverable, ] -} -#endif // swift(>=4.2) +} /// /// Enum to indicate to clients whether this firmware is a special firmware build, like an event. /// The first 16 values are reserved for non-event special firmwares, like the Smart Citizen use case. -public enum FirmwareEdition: SwiftProtobuf.Enum { +public enum FirmwareEdition: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1149,11 +1174,6 @@ public enum FirmwareEdition: SwiftProtobuf.Enum { } } -} - -#if swift(>=4.2) - -extension FirmwareEdition: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [FirmwareEdition] = [ .vanilla, @@ -1164,15 +1184,14 @@ extension FirmwareEdition: CaseIterable { .hamvention, .diyEdition, ] -} -#endif // swift(>=4.2) +} /// /// Enum for modules excluded from a device's configuration. /// Each value represents a ModuleConfigType that can be toggled as excluded /// by setting its corresponding bit in the `excluded_modules` bitmask field. -public enum ExcludedModules: SwiftProtobuf.Enum { +public enum ExcludedModules: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1288,11 +1307,6 @@ public enum ExcludedModules: SwiftProtobuf.Enum { } } -} - -#if swift(>=4.2) - -extension ExcludedModules: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [ExcludedModules] = [ .excludedNone, @@ -1312,13 +1326,12 @@ extension ExcludedModules: CaseIterable { .bluetoothConfig, .networkConfig, ] -} -#endif // swift(>=4.2) +} /// /// A GPS Position -public struct Position { +public struct Position: @unchecked Sendable { // 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. @@ -1535,7 +1548,7 @@ public struct Position { /// /// How the location was acquired: manual, onboard GPS, external (EUD) GPS - public enum LocSource: SwiftProtobuf.Enum { + public enum LocSource: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1579,12 +1592,20 @@ public struct Position { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Position.LocSource] = [ + .locUnset, + .locManual, + .locInternal, + .locExternal, + ] + } /// /// How the altitude was acquired: manual, GPS int/ext, etc /// Default: same as location_source if present - public enum AltSource: SwiftProtobuf.Enum { + public enum AltSource: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1634,6 +1655,15 @@ public struct Position { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Position.AltSource] = [ + .altUnset, + .altManual, + .altInternal, + .altExternal, + .altBarometric, + ] + } public init() {} @@ -1641,31 +1671,6 @@ public struct Position { fileprivate var _storage = _StorageClass.defaultInstance } -#if swift(>=4.2) - -extension Position.LocSource: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Position.LocSource] = [ - .locUnset, - .locManual, - .locInternal, - .locExternal, - ] -} - -extension Position.AltSource: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Position.AltSource] = [ - .altUnset, - .altManual, - .altInternal, - .altExternal, - .altBarometric, - ] -} - -#endif // swift(>=4.2) - /// /// Broadcast when a newly powered mesh node wants to find a node num it can use /// Sent from the phone over bluetooth to set the user id for the owner of this node. @@ -1687,7 +1692,7 @@ extension Position.AltSource: CaseIterable { /// A few nodenums are reserved and will never be requested: /// 0xff - broadcast /// 0 through 3 - for future use -public struct User { +public struct User: Sendable { // 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. @@ -1712,6 +1717,8 @@ public struct User { /// Deprecated in Meshtastic 2.1.x /// This is the addr of the radio. /// Not populated by the phone, but added by the esp32 when broadcasting + /// + /// NOTE: This field was marked as deprecated in the .proto file. public var macaddr: Data = Data() /// @@ -1756,7 +1763,7 @@ public struct User { /// /// A message used in a traceroute -public struct RouteDiscovery { +public struct RouteDiscovery: Sendable { // 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. @@ -1784,7 +1791,7 @@ public struct RouteDiscovery { /// /// A Routing control Data packet handled by the routing module -public struct Routing { +public struct Routing: Sendable { // 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. @@ -1824,7 +1831,7 @@ public struct Routing { public var unknownFields = SwiftProtobuf.UnknownStorage() - public enum OneOf_Variant: Equatable { + public enum OneOf_Variant: Equatable, Sendable { /// /// A route request going from the requester case routeRequest(RouteDiscovery) @@ -1836,34 +1843,12 @@ public struct Routing { /// in addition to ack.fail_id to provide details on the type of failure). case errorReason(Routing.Error) - #if !swift(>=4.1) - public static func ==(lhs: Routing.OneOf_Variant, rhs: Routing.OneOf_Variant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.routeRequest, .routeRequest): return { - guard case .routeRequest(let l) = lhs, case .routeRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.routeReply, .routeReply): return { - guard case .routeReply(let l) = lhs, case .routeReply(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.errorReason, .errorReason): return { - guard case .errorReason(let l) = lhs, case .errorReason(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } /// /// A failure in delivering a message (usually used for routing control messages, but might be provided in addition to ack.fail_id to provide /// details on the type of failure). - public enum Error: SwiftProtobuf.Enum { + public enum Error: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1988,43 +1973,37 @@ public struct Routing { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Routing.Error] = [ + .none, + .noRoute, + .gotNak, + .timeout, + .noInterface, + .maxRetransmit, + .noChannel, + .tooLarge, + .noResponse, + .dutyCycleLimit, + .badRequest, + .notAuthorized, + .pkiFailed, + .pkiUnknownPubkey, + .adminBadSessionKey, + .adminPublicKeyUnauthorized, + .rateLimitExceeded, + ] + } public init() {} } -#if swift(>=4.2) - -extension Routing.Error: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Routing.Error] = [ - .none, - .noRoute, - .gotNak, - .timeout, - .noInterface, - .maxRetransmit, - .noChannel, - .tooLarge, - .noResponse, - .dutyCycleLimit, - .badRequest, - .notAuthorized, - .pkiFailed, - .pkiUnknownPubkey, - .adminBadSessionKey, - .adminPublicKeyUnauthorized, - .rateLimitExceeded, - ] -} - -#endif // swift(>=4.2) - /// /// (Formerly called SubPacket) /// The payload portion fo a packet, this is the actual bytes that are sent /// inside a radio packet (because from/to are broken out by the comms library) -public struct DataMessage { +public struct DataMessage: Sendable { // 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. @@ -2091,7 +2070,7 @@ public struct DataMessage { /// /// The actual over-the-mesh message doing KeyVerification -public struct KeyVerification { +public struct KeyVerification: Sendable { // 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. @@ -2114,9 +2093,127 @@ public struct KeyVerification { public init() {} } +/// +/// The actual over-the-mesh message doing store and forward++ +public struct StoreForwardPlusPlus: Sendable { + // 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. + + /// + /// Which message type is this + public var sfppMessageType: StoreForwardPlusPlus.SFPP_message_type = .canonAnnounce + + /// + /// The hash of the specific message + public var messageHash: Data = Data() + + /// + /// The hash of a link on a chain + public var commitHash: Data = Data() + + /// + /// the root hash of a chain + public var rootHash: Data = Data() + + /// + /// The encrypted bytes from a message + public var message: Data = Data() + + /// + /// Message ID of the contained message + public var encapsulatedID: UInt32 = 0 + + /// + /// Destination of the contained message + public var encapsulatedTo: UInt32 = 0 + + /// + /// Sender of the contained message + public var encapsulatedFrom: UInt32 = 0 + + /// + /// The receive time of the message in question + public var encapsulatedRxtime: UInt32 = 0 + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + /// + /// Enum of message types + public enum SFPP_message_type: SwiftProtobuf.Enum, Swift.CaseIterable { + public typealias RawValue = Int + + /// + /// Send an announcement of the canonical tip of a chain + case canonAnnounce // = 0 + + /// + /// Query whether a specific link is on the chain + case chainQuery // = 1 + + /// + /// Request the next link in the chain + case linkRequest // = 3 + + /// + /// Provide a link to add to the chain + case linkProvide // = 4 + + /// + /// If we must fragment, send the first half + case linkProvideFirsthalf // = 5 + + /// + /// If we must fragment, send the second half + case linkProvideSecondhalf // = 6 + case UNRECOGNIZED(Int) + + public init() { + self = .canonAnnounce + } + + public init?(rawValue: Int) { + switch rawValue { + case 0: self = .canonAnnounce + case 1: self = .chainQuery + case 3: self = .linkRequest + case 4: self = .linkProvide + case 5: self = .linkProvideFirsthalf + case 6: self = .linkProvideSecondhalf + default: self = .UNRECOGNIZED(rawValue) + } + } + + public var rawValue: Int { + switch self { + case .canonAnnounce: return 0 + case .chainQuery: return 1 + case .linkRequest: return 3 + case .linkProvide: return 4 + case .linkProvideFirsthalf: return 5 + case .linkProvideSecondhalf: return 6 + case .UNRECOGNIZED(let i): return i + } + } + + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [StoreForwardPlusPlus.SFPP_message_type] = [ + .canonAnnounce, + .chainQuery, + .linkRequest, + .linkProvide, + .linkProvideFirsthalf, + .linkProvideSecondhalf, + ] + + } + + public init() {} +} + /// /// Waypoint message, used to share arbitrary locations across the mesh -public struct Waypoint { +public struct Waypoint: Sendable { // 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. @@ -2178,7 +2275,7 @@ public struct Waypoint { /// /// This message will be proxied over the PhoneAPI for the client to deliver to the MQTT server -public struct MqttClientProxyMessage { +public struct MqttClientProxyMessage: Sendable { // 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. @@ -2219,7 +2316,7 @@ public struct MqttClientProxyMessage { /// /// The actual service envelope payload or text for mqtt pub / sub - public enum OneOf_PayloadVariant: Equatable { + public enum OneOf_PayloadVariant: Equatable, Sendable { /// /// Bytes case data(Data) @@ -2227,24 +2324,6 @@ public struct MqttClientProxyMessage { /// Text case text(String) - #if !swift(>=4.1) - public static func ==(lhs: MqttClientProxyMessage.OneOf_PayloadVariant, rhs: MqttClientProxyMessage.OneOf_PayloadVariant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.data, .data): return { - guard case .data(let l) = lhs, case .data(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.text, .text): return { - guard case .text(let l) = lhs, case .text(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } public init() {} @@ -2254,7 +2333,7 @@ public struct MqttClientProxyMessage { /// A packet envelope sent/received over the mesh /// only payload_variant is sent in the payload portion of the LORA packet. /// The other fields are either not sent at all, or sent in the special 16 byte LORA header. -public struct MeshPacket { +public struct MeshPacket: @unchecked Sendable { // 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. @@ -2270,6 +2349,10 @@ public struct MeshPacket { /// /// The (immediate) destination for this packet + /// If the value is 4,294,967,295 (maximum value of an unsigned 32bit integer), this indicates that the packet was + /// not destined for a specific node, but for a channel as indicated by the value of `channel` below. + /// If the value is another, this indicates that the packet was destined for a specific + /// node (i.e. a kind of "Direct Message" to this node) and not broadcast on a channel. public var to: UInt32 { get {return _storage._to} set {_uniqueStorage()._to = newValue} @@ -2388,6 +2471,8 @@ public struct MeshPacket { /// /// Describe if this message is delayed + /// + /// NOTE: This field was marked as deprecated in the .proto file. public var delayed: MeshPacket.Delayed { get {return _storage._delayed} set {_uniqueStorage()._delayed = newValue} @@ -2456,7 +2541,7 @@ public struct MeshPacket { public var unknownFields = SwiftProtobuf.UnknownStorage() - public enum OneOf_PayloadVariant: Equatable { + public enum OneOf_PayloadVariant: Equatable, Sendable { /// /// TODO: REPLACE case decoded(DataMessage) @@ -2464,24 +2549,6 @@ public struct MeshPacket { /// TODO: REPLACE case encrypted(Data) - #if !swift(>=4.1) - public static func ==(lhs: MeshPacket.OneOf_PayloadVariant, rhs: MeshPacket.OneOf_PayloadVariant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.decoded, .decoded): return { - guard case .decoded(let l) = lhs, case .decoded(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.encrypted, .encrypted): return { - guard case .encrypted(let l) = lhs, case .encrypted(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } /// @@ -2503,7 +2570,7 @@ public struct MeshPacket { /// So I bit the bullet and implemented a new (internal - not sent over the air) /// field in MeshPacket called 'priority'. /// And the transmission queue in the router object is now a priority queue. - public enum Priority: SwiftProtobuf.Enum { + public enum Priority: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -2587,11 +2654,25 @@ public struct MeshPacket { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [MeshPacket.Priority] = [ + .unset, + .min, + .background, + .default, + .reliable, + .response, + .high, + .alert, + .ack, + .max, + ] + } /// /// Identify if this is a delayed packet - public enum Delayed: SwiftProtobuf.Enum { + public enum Delayed: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -2629,11 +2710,18 @@ public struct MeshPacket { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [MeshPacket.Delayed] = [ + .noDelay, + .broadcast, + .direct, + ] + } /// /// Enum to identify which transport mechanism this packet arrived over - public enum TransportMechanism: SwiftProtobuf.Enum { + public enum TransportMechanism: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -2701,6 +2789,18 @@ public struct MeshPacket { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [MeshPacket.TransportMechanism] = [ + .transportInternal, + .transportLora, + .transportLoraAlt1, + .transportLoraAlt2, + .transportLoraAlt3, + .transportMqtt, + .transportMulticastUdp, + .transportApi, + ] + } public init() {} @@ -2708,49 +2808,6 @@ public struct MeshPacket { fileprivate var _storage = _StorageClass.defaultInstance } -#if swift(>=4.2) - -extension MeshPacket.Priority: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [MeshPacket.Priority] = [ - .unset, - .min, - .background, - .default, - .reliable, - .response, - .high, - .alert, - .ack, - .max, - ] -} - -extension MeshPacket.Delayed: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [MeshPacket.Delayed] = [ - .noDelay, - .broadcast, - .direct, - ] -} - -extension MeshPacket.TransportMechanism: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [MeshPacket.TransportMechanism] = [ - .transportInternal, - .transportLora, - .transportLoraAlt1, - .transportLoraAlt2, - .transportLoraAlt3, - .transportMqtt, - .transportMulticastUdp, - .transportApi, - ] -} - -#endif // swift(>=4.2) - /// /// The bluetooth to device link: /// Old BTLE protocol docs from TODO, merge in above and make real docs... @@ -2768,7 +2825,7 @@ extension MeshPacket.TransportMechanism: CaseIterable { /// level etc) SET_CONFIG (switches device to a new set of radio params and /// preshared key, drops all existing nodes, force our node to rejoin this new group) /// Full information about a node on the mesh -public struct NodeInfo { +public struct NodeInfo: @unchecked Sendable { // 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. @@ -2890,7 +2947,7 @@ public struct NodeInfo { /// Unique local debugging info for this node /// Note: we don't include position or the user info, because that will come in the /// Sent to the phone in response to WantNodes. -public struct MyNodeInfo { +public struct MyNodeInfo: Sendable { // 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. @@ -2938,7 +2995,7 @@ public struct MyNodeInfo { /// on the message it is assumed to be a continuation of the previously sent message. /// This allows the device code to use fixed maxlen 64 byte strings for messages, /// and then extend as needed by emitting multiple records. -public struct LogRecord { +public struct LogRecord: Sendable { // 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. @@ -2963,7 +3020,7 @@ public struct LogRecord { /// /// Log levels, chosen to match python logging conventions. - public enum Level: SwiftProtobuf.Enum { + public enum Level: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -3025,29 +3082,23 @@ public struct LogRecord { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [LogRecord.Level] = [ + .unset, + .critical, + .error, + .warning, + .info, + .debug, + .trace, + ] + } public init() {} } -#if swift(>=4.2) - -extension LogRecord.Level: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [LogRecord.Level] = [ - .unset, - .critical, - .error, - .warning, - .info, - .debug, - .trace, - ] -} - -#endif // swift(>=4.2) - -public struct QueueStatus { +public struct QueueStatus: Sendable { // 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. @@ -3074,7 +3125,7 @@ public struct QueueStatus { /// It will support READ and NOTIFY. When a new packet arrives the device will BLE notify? /// It will sit in that descriptor until consumed by the phone, /// at which point the next item in the FIFO will be populated. -public struct FromRadio { +public struct FromRadio: Sendable { // 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. @@ -3260,7 +3311,7 @@ public struct FromRadio { /// /// Log levels, chosen to match python logging conventions. - public enum OneOf_PayloadVariant: Equatable { + public enum OneOf_PayloadVariant: Equatable, Sendable { /// /// Log levels, chosen to match python logging conventions. case packet(MeshPacket) @@ -3318,80 +3369,6 @@ public struct FromRadio { /// Persistent data for device-ui case deviceuiConfig(DeviceUIConfig) - #if !swift(>=4.1) - public static func ==(lhs: FromRadio.OneOf_PayloadVariant, rhs: FromRadio.OneOf_PayloadVariant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.packet, .packet): return { - guard case .packet(let l) = lhs, case .packet(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.myInfo, .myInfo): return { - guard case .myInfo(let l) = lhs, case .myInfo(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.nodeInfo, .nodeInfo): return { - guard case .nodeInfo(let l) = lhs, case .nodeInfo(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.config, .config): return { - guard case .config(let l) = lhs, case .config(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.logRecord, .logRecord): return { - guard case .logRecord(let l) = lhs, case .logRecord(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.configCompleteID, .configCompleteID): return { - guard case .configCompleteID(let l) = lhs, case .configCompleteID(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.rebooted, .rebooted): return { - guard case .rebooted(let l) = lhs, case .rebooted(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.moduleConfig, .moduleConfig): return { - guard case .moduleConfig(let l) = lhs, case .moduleConfig(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.channel, .channel): return { - guard case .channel(let l) = lhs, case .channel(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.queueStatus, .queueStatus): return { - guard case .queueStatus(let l) = lhs, case .queueStatus(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.xmodemPacket, .xmodemPacket): return { - guard case .xmodemPacket(let l) = lhs, case .xmodemPacket(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.metadata, .metadata): return { - guard case .metadata(let l) = lhs, case .metadata(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.mqttClientProxyMessage, .mqttClientProxyMessage): return { - guard case .mqttClientProxyMessage(let l) = lhs, case .mqttClientProxyMessage(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.fileInfo, .fileInfo): return { - guard case .fileInfo(let l) = lhs, case .fileInfo(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.clientNotification, .clientNotification): return { - guard case .clientNotification(let l) = lhs, case .clientNotification(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.deviceuiConfig, .deviceuiConfig): return { - guard case .deviceuiConfig(let l) = lhs, case .deviceuiConfig(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } public init() {} @@ -3402,7 +3379,7 @@ public struct FromRadio { /// To be used for important messages that should to be displayed to the user /// in the form of push notifications or validation messages when saving /// invalid configuration. -public struct ClientNotification { +public struct ClientNotification: Sendable { // 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. @@ -3474,43 +3451,13 @@ public struct ClientNotification { public var unknownFields = SwiftProtobuf.UnknownStorage() - public enum OneOf_PayloadVariant: Equatable { + public enum OneOf_PayloadVariant: Equatable, Sendable { case keyVerificationNumberInform(KeyVerificationNumberInform) case keyVerificationNumberRequest(KeyVerificationNumberRequest) case keyVerificationFinal(KeyVerificationFinal) case duplicatedPublicKey(DuplicatedPublicKey) case lowEntropyKey(LowEntropyKey) - #if !swift(>=4.1) - public static func ==(lhs: ClientNotification.OneOf_PayloadVariant, rhs: ClientNotification.OneOf_PayloadVariant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.keyVerificationNumberInform, .keyVerificationNumberInform): return { - guard case .keyVerificationNumberInform(let l) = lhs, case .keyVerificationNumberInform(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.keyVerificationNumberRequest, .keyVerificationNumberRequest): return { - guard case .keyVerificationNumberRequest(let l) = lhs, case .keyVerificationNumberRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.keyVerificationFinal, .keyVerificationFinal): return { - guard case .keyVerificationFinal(let l) = lhs, case .keyVerificationFinal(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.duplicatedPublicKey, .duplicatedPublicKey): return { - guard case .duplicatedPublicKey(let l) = lhs, case .duplicatedPublicKey(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.lowEntropyKey, .lowEntropyKey): return { - guard case .lowEntropyKey(let l) = lhs, case .lowEntropyKey(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } public init() {} @@ -3518,7 +3465,7 @@ public struct ClientNotification { fileprivate var _replyID: UInt32? = nil } -public struct KeyVerificationNumberInform { +public struct KeyVerificationNumberInform: Sendable { // 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. @@ -3534,7 +3481,7 @@ public struct KeyVerificationNumberInform { public init() {} } -public struct KeyVerificationNumberRequest { +public struct KeyVerificationNumberRequest: Sendable { // 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. @@ -3548,7 +3495,7 @@ public struct KeyVerificationNumberRequest { public init() {} } -public struct KeyVerificationFinal { +public struct KeyVerificationFinal: Sendable { // 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. @@ -3566,7 +3513,7 @@ public struct KeyVerificationFinal { public init() {} } -public struct DuplicatedPublicKey { +public struct DuplicatedPublicKey: Sendable { // 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. @@ -3576,7 +3523,7 @@ public struct DuplicatedPublicKey { public init() {} } -public struct LowEntropyKey { +public struct LowEntropyKey: Sendable { // 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. @@ -3588,7 +3535,7 @@ public struct LowEntropyKey { /// /// Individual File info for the device -public struct FileInfo { +public struct FileInfo: Sendable { // 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. @@ -3609,7 +3556,7 @@ public struct FileInfo { /// /// Packets/commands to the radio will be written (reliably) to the toRadio characteristic. /// Once the write completes the phone can assume it is handled. -public struct ToRadio { +public struct ToRadio: Sendable { // 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. @@ -3689,7 +3636,7 @@ public struct ToRadio { /// /// Log levels, chosen to match python logging conventions. - public enum OneOf_PayloadVariant: Equatable { + public enum OneOf_PayloadVariant: Equatable, Sendable { /// /// Send this packet on the mesh case packet(MeshPacket) @@ -3716,40 +3663,6 @@ public struct ToRadio { /// Heartbeat message (used to keep the device connection awake on serial) case heartbeat(Heartbeat) - #if !swift(>=4.1) - public static func ==(lhs: ToRadio.OneOf_PayloadVariant, rhs: ToRadio.OneOf_PayloadVariant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.packet, .packet): return { - guard case .packet(let l) = lhs, case .packet(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.wantConfigID, .wantConfigID): return { - guard case .wantConfigID(let l) = lhs, case .wantConfigID(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.disconnect, .disconnect): return { - guard case .disconnect(let l) = lhs, case .disconnect(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.xmodemPacket, .xmodemPacket): return { - guard case .xmodemPacket(let l) = lhs, case .xmodemPacket(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.mqttClientProxyMessage, .mqttClientProxyMessage): return { - guard case .mqttClientProxyMessage(let l) = lhs, case .mqttClientProxyMessage(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.heartbeat, .heartbeat): return { - guard case .heartbeat(let l) = lhs, case .heartbeat(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } public init() {} @@ -3757,7 +3670,7 @@ public struct ToRadio { /// /// Compressed message payload -public struct Compressed { +public struct Compressed: Sendable { // 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. @@ -3777,7 +3690,7 @@ public struct Compressed { /// /// Full info on edges for a single node -public struct NeighborInfo { +public struct NeighborInfo: Sendable { // 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. @@ -3805,7 +3718,7 @@ public struct NeighborInfo { /// /// A single edge in the mesh -public struct Neighbor { +public struct Neighbor: Sendable { // 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. @@ -3835,7 +3748,7 @@ public struct Neighbor { /// /// Device metadata response -public struct DeviceMetadata { +public struct DeviceMetadata: Sendable { // 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. @@ -3897,7 +3810,7 @@ public struct DeviceMetadata { /// /// A heartbeat message is sent to the node from the client to keep the connection alive. /// This is currently only needed to keep serial connections alive, but can be used by any PhoneAPI. -public struct Heartbeat { +public struct Heartbeat: Sendable { // 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. @@ -3913,7 +3826,7 @@ public struct Heartbeat { /// /// RemoteHardwarePins associated with a node -public struct NodeRemoteHardwarePin { +public struct NodeRemoteHardwarePin: Sendable { // 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. @@ -3940,7 +3853,7 @@ public struct NodeRemoteHardwarePin { fileprivate var _pin: RemoteHardwarePin? = nil } -public struct ChunkedPayload { +public struct ChunkedPayload: Sendable { // 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. @@ -3968,7 +3881,7 @@ public struct ChunkedPayload { /// /// Wrapper message for broken repeated oneof support -public struct resend_chunks { +public struct resend_chunks: Sendable { // 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. @@ -3982,7 +3895,7 @@ public struct resend_chunks { /// /// Responses to a ChunkedPayload request -public struct ChunkedPayloadResponse { +public struct ChunkedPayloadResponse: Sendable { // 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. @@ -4025,7 +3938,7 @@ public struct ChunkedPayloadResponse { public var unknownFields = SwiftProtobuf.UnknownStorage() - public enum OneOf_PayloadVariant: Equatable { + public enum OneOf_PayloadVariant: Equatable, Sendable { /// /// Request to transfer chunked payload case requestTransfer(Bool) @@ -4036,297 +3949,38 @@ public struct ChunkedPayloadResponse { /// Request missing indexes in the chunked payload case resendChunks(resend_chunks) - #if !swift(>=4.1) - public static func ==(lhs: ChunkedPayloadResponse.OneOf_PayloadVariant, rhs: ChunkedPayloadResponse.OneOf_PayloadVariant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.requestTransfer, .requestTransfer): return { - guard case .requestTransfer(let l) = lhs, case .requestTransfer(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.acceptTransfer, .acceptTransfer): return { - guard case .acceptTransfer(let l) = lhs, case .acceptTransfer(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.resendChunks, .resendChunks): return { - guard case .resendChunks(let l) = lhs, case .resendChunks(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } public init() {} } -#if swift(>=5.5) && canImport(_Concurrency) -extension HardwareModel: @unchecked Sendable {} -extension Constants: @unchecked Sendable {} -extension CriticalErrorCode: @unchecked Sendable {} -extension FirmwareEdition: @unchecked Sendable {} -extension ExcludedModules: @unchecked Sendable {} -extension Position: @unchecked Sendable {} -extension Position.LocSource: @unchecked Sendable {} -extension Position.AltSource: @unchecked Sendable {} -extension User: @unchecked Sendable {} -extension RouteDiscovery: @unchecked Sendable {} -extension Routing: @unchecked Sendable {} -extension Routing.OneOf_Variant: @unchecked Sendable {} -extension Routing.Error: @unchecked Sendable {} -extension DataMessage: @unchecked Sendable {} -extension KeyVerification: @unchecked Sendable {} -extension Waypoint: @unchecked Sendable {} -extension MqttClientProxyMessage: @unchecked Sendable {} -extension MqttClientProxyMessage.OneOf_PayloadVariant: @unchecked Sendable {} -extension MeshPacket: @unchecked Sendable {} -extension MeshPacket.OneOf_PayloadVariant: @unchecked Sendable {} -extension MeshPacket.Priority: @unchecked Sendable {} -extension MeshPacket.Delayed: @unchecked Sendable {} -extension MeshPacket.TransportMechanism: @unchecked Sendable {} -extension NodeInfo: @unchecked Sendable {} -extension MyNodeInfo: @unchecked Sendable {} -extension LogRecord: @unchecked Sendable {} -extension LogRecord.Level: @unchecked Sendable {} -extension QueueStatus: @unchecked Sendable {} -extension FromRadio: @unchecked Sendable {} -extension FromRadio.OneOf_PayloadVariant: @unchecked Sendable {} -extension ClientNotification: @unchecked Sendable {} -extension ClientNotification.OneOf_PayloadVariant: @unchecked Sendable {} -extension KeyVerificationNumberInform: @unchecked Sendable {} -extension KeyVerificationNumberRequest: @unchecked Sendable {} -extension KeyVerificationFinal: @unchecked Sendable {} -extension DuplicatedPublicKey: @unchecked Sendable {} -extension LowEntropyKey: @unchecked Sendable {} -extension FileInfo: @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 {} -extension Heartbeat: @unchecked Sendable {} -extension NodeRemoteHardwarePin: @unchecked Sendable {} -extension ChunkedPayload: @unchecked Sendable {} -extension resend_chunks: @unchecked Sendable {} -extension ChunkedPayloadResponse: @unchecked Sendable {} -extension ChunkedPayloadResponse.OneOf_PayloadVariant: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" extension HardwareModel: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "UNSET"), - 1: .same(proto: "TLORA_V2"), - 2: .same(proto: "TLORA_V1"), - 3: .same(proto: "TLORA_V2_1_1P6"), - 4: .same(proto: "TBEAM"), - 5: .same(proto: "HELTEC_V2_0"), - 6: .same(proto: "TBEAM_V0P7"), - 7: .same(proto: "T_ECHO"), - 8: .same(proto: "TLORA_V1_1P3"), - 9: .same(proto: "RAK4631"), - 10: .same(proto: "HELTEC_V2_1"), - 11: .same(proto: "HELTEC_V1"), - 12: .same(proto: "LILYGO_TBEAM_S3_CORE"), - 13: .same(proto: "RAK11200"), - 14: .same(proto: "NANO_G1"), - 15: .same(proto: "TLORA_V2_1_1P8"), - 16: .same(proto: "TLORA_T3_S3"), - 17: .same(proto: "NANO_G1_EXPLORER"), - 18: .same(proto: "NANO_G2_ULTRA"), - 19: .same(proto: "LORA_TYPE"), - 20: .same(proto: "WIPHONE"), - 21: .same(proto: "WIO_WM1110"), - 22: .same(proto: "RAK2560"), - 23: .same(proto: "HELTEC_HRU_3601"), - 24: .same(proto: "HELTEC_WIRELESS_BRIDGE"), - 25: .same(proto: "STATION_G1"), - 26: .same(proto: "RAK11310"), - 27: .same(proto: "SENSELORA_RP2040"), - 28: .same(proto: "SENSELORA_S3"), - 29: .same(proto: "CANARYONE"), - 30: .same(proto: "RP2040_LORA"), - 31: .same(proto: "STATION_G2"), - 32: .same(proto: "LORA_RELAY_V1"), - 33: .same(proto: "NRF52840DK"), - 34: .same(proto: "PPR"), - 35: .same(proto: "GENIEBLOCKS"), - 36: .same(proto: "NRF52_UNKNOWN"), - 37: .same(proto: "PORTDUINO"), - 38: .same(proto: "ANDROID_SIM"), - 39: .same(proto: "DIY_V1"), - 40: .same(proto: "NRF52840_PCA10059"), - 41: .same(proto: "DR_DEV"), - 42: .same(proto: "M5STACK"), - 43: .same(proto: "HELTEC_V3"), - 44: .same(proto: "HELTEC_WSL_V3"), - 45: .same(proto: "BETAFPV_2400_TX"), - 46: .same(proto: "BETAFPV_900_NANO_TX"), - 47: .same(proto: "RPI_PICO"), - 48: .same(proto: "HELTEC_WIRELESS_TRACKER"), - 49: .same(proto: "HELTEC_WIRELESS_PAPER"), - 50: .same(proto: "T_DECK"), - 51: .same(proto: "T_WATCH_S3"), - 52: .same(proto: "PICOMPUTER_S3"), - 53: .same(proto: "HELTEC_HT62"), - 54: .same(proto: "EBYTE_ESP32_S3"), - 55: .same(proto: "ESP32_S3_PICO"), - 56: .same(proto: "CHATTER_2"), - 57: .same(proto: "HELTEC_WIRELESS_PAPER_V1_0"), - 58: .same(proto: "HELTEC_WIRELESS_TRACKER_V1_0"), - 59: .same(proto: "UNPHONE"), - 60: .same(proto: "TD_LORAC"), - 61: .same(proto: "CDEBYTE_EORA_S3"), - 62: .same(proto: "TWC_MESH_V4"), - 63: .same(proto: "NRF52_PROMICRO_DIY"), - 64: .same(proto: "RADIOMASTER_900_BANDIT_NANO"), - 65: .same(proto: "HELTEC_CAPSULE_SENSOR_V3"), - 66: .same(proto: "HELTEC_VISION_MASTER_T190"), - 67: .same(proto: "HELTEC_VISION_MASTER_E213"), - 68: .same(proto: "HELTEC_VISION_MASTER_E290"), - 69: .same(proto: "HELTEC_MESH_NODE_T114"), - 70: .same(proto: "SENSECAP_INDICATOR"), - 71: .same(proto: "TRACKER_T1000_E"), - 72: .same(proto: "RAK3172"), - 73: .same(proto: "WIO_E5"), - 74: .same(proto: "RADIOMASTER_900_BANDIT"), - 75: .same(proto: "ME25LS01_4Y10TD"), - 76: .same(proto: "RP2040_FEATHER_RFM95"), - 77: .same(proto: "M5STACK_COREBASIC"), - 78: .same(proto: "M5STACK_CORE2"), - 79: .same(proto: "RPI_PICO2"), - 80: .same(proto: "M5STACK_CORES3"), - 81: .same(proto: "SEEED_XIAO_S3"), - 82: .same(proto: "MS24SF1"), - 83: .same(proto: "TLORA_C6"), - 84: .same(proto: "WISMESH_TAP"), - 85: .same(proto: "ROUTASTIC"), - 86: .same(proto: "MESH_TAB"), - 87: .same(proto: "MESHLINK"), - 88: .same(proto: "XIAO_NRF52_KIT"), - 89: .same(proto: "THINKNODE_M1"), - 90: .same(proto: "THINKNODE_M2"), - 91: .same(proto: "T_ETH_ELITE"), - 92: .same(proto: "HELTEC_SENSOR_HUB"), - 93: .same(proto: "RESERVED_FRIED_CHICKEN"), - 94: .same(proto: "HELTEC_MESH_POCKET"), - 95: .same(proto: "SEEED_SOLAR_NODE"), - 96: .same(proto: "NOMADSTAR_METEOR_PRO"), - 97: .same(proto: "CROWPANEL"), - 98: .same(proto: "LINK_32"), - 99: .same(proto: "SEEED_WIO_TRACKER_L1"), - 100: .same(proto: "SEEED_WIO_TRACKER_L1_EINK"), - 101: .same(proto: "MUZI_R1_NEO"), - 102: .same(proto: "T_DECK_PRO"), - 103: .same(proto: "T_LORA_PAGER"), - 104: .same(proto: "M5STACK_RESERVED"), - 105: .same(proto: "WISMESH_TAG"), - 106: .same(proto: "RAK3312"), - 107: .same(proto: "THINKNODE_M5"), - 108: .same(proto: "HELTEC_MESH_SOLAR"), - 109: .same(proto: "T_ECHO_LITE"), - 110: .same(proto: "HELTEC_V4"), - 111: .same(proto: "M5STACK_C6L"), - 112: .same(proto: "M5STACK_CARDPUTER_ADV"), - 113: .same(proto: "HELTEC_WIRELESS_TRACKER_V2"), - 114: .same(proto: "T_WATCH_ULTRA"), - 255: .same(proto: "PRIVATE_HW"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0UNSET\0\u{1}TLORA_V2\0\u{1}TLORA_V1\0\u{1}TLORA_V2_1_1P6\0\u{1}TBEAM\0\u{1}HELTEC_V2_0\0\u{1}TBEAM_V0P7\0\u{1}T_ECHO\0\u{1}TLORA_V1_1P3\0\u{1}RAK4631\0\u{1}HELTEC_V2_1\0\u{1}HELTEC_V1\0\u{1}LILYGO_TBEAM_S3_CORE\0\u{1}RAK11200\0\u{1}NANO_G1\0\u{1}TLORA_V2_1_1P8\0\u{1}TLORA_T3_S3\0\u{1}NANO_G1_EXPLORER\0\u{1}NANO_G2_ULTRA\0\u{1}LORA_TYPE\0\u{1}WIPHONE\0\u{1}WIO_WM1110\0\u{1}RAK2560\0\u{1}HELTEC_HRU_3601\0\u{1}HELTEC_WIRELESS_BRIDGE\0\u{1}STATION_G1\0\u{1}RAK11310\0\u{1}SENSELORA_RP2040\0\u{1}SENSELORA_S3\0\u{1}CANARYONE\0\u{1}RP2040_LORA\0\u{1}STATION_G2\0\u{1}LORA_RELAY_V1\0\u{1}NRF52840DK\0\u{1}PPR\0\u{1}GENIEBLOCKS\0\u{1}NRF52_UNKNOWN\0\u{1}PORTDUINO\0\u{1}ANDROID_SIM\0\u{1}DIY_V1\0\u{1}NRF52840_PCA10059\0\u{1}DR_DEV\0\u{1}M5STACK\0\u{1}HELTEC_V3\0\u{1}HELTEC_WSL_V3\0\u{1}BETAFPV_2400_TX\0\u{1}BETAFPV_900_NANO_TX\0\u{1}RPI_PICO\0\u{1}HELTEC_WIRELESS_TRACKER\0\u{1}HELTEC_WIRELESS_PAPER\0\u{1}T_DECK\0\u{1}T_WATCH_S3\0\u{1}PICOMPUTER_S3\0\u{1}HELTEC_HT62\0\u{1}EBYTE_ESP32_S3\0\u{1}ESP32_S3_PICO\0\u{1}CHATTER_2\0\u{1}HELTEC_WIRELESS_PAPER_V1_0\0\u{1}HELTEC_WIRELESS_TRACKER_V1_0\0\u{1}UNPHONE\0\u{1}TD_LORAC\0\u{1}CDEBYTE_EORA_S3\0\u{1}TWC_MESH_V4\0\u{1}NRF52_PROMICRO_DIY\0\u{1}RADIOMASTER_900_BANDIT_NANO\0\u{1}HELTEC_CAPSULE_SENSOR_V3\0\u{1}HELTEC_VISION_MASTER_T190\0\u{1}HELTEC_VISION_MASTER_E213\0\u{1}HELTEC_VISION_MASTER_E290\0\u{1}HELTEC_MESH_NODE_T114\0\u{1}SENSECAP_INDICATOR\0\u{1}TRACKER_T1000_E\0\u{1}RAK3172\0\u{1}WIO_E5\0\u{1}RADIOMASTER_900_BANDIT\0\u{1}ME25LS01_4Y10TD\0\u{1}RP2040_FEATHER_RFM95\0\u{1}M5STACK_COREBASIC\0\u{1}M5STACK_CORE2\0\u{1}RPI_PICO2\0\u{1}M5STACK_CORES3\0\u{1}SEEED_XIAO_S3\0\u{1}MS24SF1\0\u{1}TLORA_C6\0\u{1}WISMESH_TAP\0\u{1}ROUTASTIC\0\u{1}MESH_TAB\0\u{1}MESHLINK\0\u{1}XIAO_NRF52_KIT\0\u{1}THINKNODE_M1\0\u{1}THINKNODE_M2\0\u{1}T_ETH_ELITE\0\u{1}HELTEC_SENSOR_HUB\0\u{1}MUZI_BASE\0\u{1}HELTEC_MESH_POCKET\0\u{1}SEEED_SOLAR_NODE\0\u{1}NOMADSTAR_METEOR_PRO\0\u{1}CROWPANEL\0\u{1}LINK_32\0\u{1}SEEED_WIO_TRACKER_L1\0\u{1}SEEED_WIO_TRACKER_L1_EINK\0\u{1}MUZI_R1_NEO\0\u{1}T_DECK_PRO\0\u{1}T_LORA_PAGER\0\u{1}M5STACK_RESERVED\0\u{1}WISMESH_TAG\0\u{1}RAK3312\0\u{1}THINKNODE_M5\0\u{1}HELTEC_MESH_SOLAR\0\u{1}T_ECHO_LITE\0\u{1}HELTEC_V4\0\u{1}M5STACK_C6L\0\u{1}M5STACK_CARDPUTER_ADV\0\u{1}HELTEC_WIRELESS_TRACKER_V2\0\u{1}T_WATCH_ULTRA\0\u{1}THINKNODE_M3\0\u{1}WISMESH_TAP_V2\0\u{1}RAK3401\0\u{1}RAK6421\0\u{1}THINKNODE_M4\0\u{1}THINKNODE_M6\0\u{2}G\u{2}PRIVATE_HW\0") } extension Constants: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "ZERO"), - 233: .same(proto: "DATA_PAYLOAD_LEN"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0ZERO\0\u{2}i\u{3}DATA_PAYLOAD_LEN\0") } extension CriticalErrorCode: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "NONE"), - 1: .same(proto: "TX_WATCHDOG"), - 2: .same(proto: "SLEEP_ENTER_WAIT"), - 3: .same(proto: "NO_RADIO"), - 4: .same(proto: "UNSPECIFIED"), - 5: .same(proto: "UBLOX_UNIT_FAILED"), - 6: .same(proto: "NO_AXP192"), - 7: .same(proto: "INVALID_RADIO_SETTING"), - 8: .same(proto: "TRANSMIT_FAILED"), - 9: .same(proto: "BROWNOUT"), - 10: .same(proto: "SX1262_FAILURE"), - 11: .same(proto: "RADIO_SPI_BUG"), - 12: .same(proto: "FLASH_CORRUPTION_RECOVERABLE"), - 13: .same(proto: "FLASH_CORRUPTION_UNRECOVERABLE"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0NONE\0\u{1}TX_WATCHDOG\0\u{1}SLEEP_ENTER_WAIT\0\u{1}NO_RADIO\0\u{1}UNSPECIFIED\0\u{1}UBLOX_UNIT_FAILED\0\u{1}NO_AXP192\0\u{1}INVALID_RADIO_SETTING\0\u{1}TRANSMIT_FAILED\0\u{1}BROWNOUT\0\u{1}SX1262_FAILURE\0\u{1}RADIO_SPI_BUG\0\u{1}FLASH_CORRUPTION_RECOVERABLE\0\u{1}FLASH_CORRUPTION_UNRECOVERABLE\0") } extension FirmwareEdition: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "VANILLA"), - 1: .same(proto: "SMART_CITIZEN"), - 16: .same(proto: "OPEN_SAUCE"), - 17: .same(proto: "DEFCON"), - 18: .same(proto: "BURNING_MAN"), - 19: .same(proto: "HAMVENTION"), - 127: .same(proto: "DIY_EDITION"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0VANILLA\0\u{1}SMART_CITIZEN\0\u{2}\u{f}OPEN_SAUCE\0\u{1}DEFCON\0\u{1}BURNING_MAN\0\u{1}HAMVENTION\0\u{2}l\u{1}DIY_EDITION\0") } extension ExcludedModules: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "EXCLUDED_NONE"), - 1: .same(proto: "MQTT_CONFIG"), - 2: .same(proto: "SERIAL_CONFIG"), - 4: .same(proto: "EXTNOTIF_CONFIG"), - 8: .same(proto: "STOREFORWARD_CONFIG"), - 16: .same(proto: "RANGETEST_CONFIG"), - 32: .same(proto: "TELEMETRY_CONFIG"), - 64: .same(proto: "CANNEDMSG_CONFIG"), - 128: .same(proto: "AUDIO_CONFIG"), - 256: .same(proto: "REMOTEHARDWARE_CONFIG"), - 512: .same(proto: "NEIGHBORINFO_CONFIG"), - 1024: .same(proto: "AMBIENTLIGHTING_CONFIG"), - 2048: .same(proto: "DETECTIONSENSOR_CONFIG"), - 4096: .same(proto: "PAXCOUNTER_CONFIG"), - 8192: .same(proto: "BLUETOOTH_CONFIG"), - 16384: .same(proto: "NETWORK_CONFIG"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0EXCLUDED_NONE\0\u{1}MQTT_CONFIG\0\u{1}SERIAL_CONFIG\0\u{2}\u{2}EXTNOTIF_CONFIG\0\u{2}\u{4}STOREFORWARD_CONFIG\0\u{2}\u{8}RANGETEST_CONFIG\0\u{2}\u{10}TELEMETRY_CONFIG\0\u{2} CANNEDMSG_CONFIG\0\u{2}@\u{1}AUDIO_CONFIG\0\u{2}@\u{2}REMOTEHARDWARE_CONFIG\0\u{2}@\u{4}NEIGHBORINFO_CONFIG\0\u{2}@\u{8}AMBIENTLIGHTING_CONFIG\0\u{2}@\u{10}DETECTIONSENSOR_CONFIG\0\u{2}@ PAXCOUNTER_CONFIG\0\u{2}@@\u{1}BLUETOOTH_CONFIG\0\u{2}@@\u{2}NETWORK_CONFIG\0") } extension Position: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Position" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "latitude_i"), - 2: .standard(proto: "longitude_i"), - 3: .same(proto: "altitude"), - 4: .same(proto: "time"), - 5: .standard(proto: "location_source"), - 6: .standard(proto: "altitude_source"), - 7: .same(proto: "timestamp"), - 8: .standard(proto: "timestamp_millis_adjust"), - 9: .standard(proto: "altitude_hae"), - 10: .standard(proto: "altitude_geoidal_separation"), - 11: .same(proto: "PDOP"), - 12: .same(proto: "HDOP"), - 13: .same(proto: "VDOP"), - 14: .standard(proto: "gps_accuracy"), - 15: .standard(proto: "ground_speed"), - 16: .standard(proto: "ground_track"), - 17: .standard(proto: "fix_quality"), - 18: .standard(proto: "fix_type"), - 19: .standard(proto: "sats_in_view"), - 20: .standard(proto: "sensor_id"), - 21: .standard(proto: "next_update"), - 22: .standard(proto: "seq_number"), - 23: .standard(proto: "precision_bits"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}latitude_i\0\u{3}longitude_i\0\u{1}altitude\0\u{1}time\0\u{3}location_source\0\u{3}altitude_source\0\u{1}timestamp\0\u{3}timestamp_millis_adjust\0\u{3}altitude_hae\0\u{3}altitude_geoidal_separation\0\u{1}PDOP\0\u{1}HDOP\0\u{1}VDOP\0\u{3}gps_accuracy\0\u{3}ground_speed\0\u{3}ground_track\0\u{3}fix_quality\0\u{3}fix_type\0\u{3}sats_in_view\0\u{3}sensor_id\0\u{3}next_update\0\u{3}seq_number\0\u{3}precision_bits\0") fileprivate class _StorageClass { var _latitudeI: Int32? = nil @@ -4353,15 +4007,11 @@ extension Position: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB var _seqNumber: UInt32 = 0 var _precisionBits: UInt32 = 0 - #if swift(>=5.10) // This property is used as the initial default value for new instances of the type. // The type itself is protecting the reference to its storage via CoW semantics. // This will force a copy to be made of this reference when the first mutation occurs; // hence, it is safe to mark this as `nonisolated(unsafe)`. static nonisolated(unsafe) let defaultInstance = _StorageClass() - #else - static let defaultInstance = _StorageClass() - #endif private init() {} @@ -4553,37 +4203,16 @@ extension Position: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB } extension Position.LocSource: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "LOC_UNSET"), - 1: .same(proto: "LOC_MANUAL"), - 2: .same(proto: "LOC_INTERNAL"), - 3: .same(proto: "LOC_EXTERNAL"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0LOC_UNSET\0\u{1}LOC_MANUAL\0\u{1}LOC_INTERNAL\0\u{1}LOC_EXTERNAL\0") } extension Position.AltSource: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "ALT_UNSET"), - 1: .same(proto: "ALT_MANUAL"), - 2: .same(proto: "ALT_INTERNAL"), - 3: .same(proto: "ALT_EXTERNAL"), - 4: .same(proto: "ALT_BAROMETRIC"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0ALT_UNSET\0\u{1}ALT_MANUAL\0\u{1}ALT_INTERNAL\0\u{1}ALT_EXTERNAL\0\u{1}ALT_BAROMETRIC\0") } extension User: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".User" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "id"), - 2: .standard(proto: "long_name"), - 3: .standard(proto: "short_name"), - 4: .same(proto: "macaddr"), - 5: .standard(proto: "hw_model"), - 6: .standard(proto: "is_licensed"), - 7: .same(proto: "role"), - 8: .standard(proto: "public_key"), - 9: .standard(proto: "is_unmessagable"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}id\0\u{3}long_name\0\u{3}short_name\0\u{1}macaddr\0\u{3}hw_model\0\u{3}is_licensed\0\u{1}role\0\u{3}public_key\0\u{3}is_unmessagable\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -4657,12 +4286,7 @@ extension User: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, extension RouteDiscovery: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".RouteDiscovery" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "route"), - 2: .standard(proto: "snr_towards"), - 3: .standard(proto: "route_back"), - 4: .standard(proto: "snr_back"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}route\0\u{3}snr_towards\0\u{3}route_back\0\u{3}snr_back\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -4707,11 +4331,7 @@ extension RouteDiscovery: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement extension Routing: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Routing" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "route_request"), - 2: .standard(proto: "route_reply"), - 3: .standard(proto: "error_reason"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}route_request\0\u{3}route_reply\0\u{3}error_reason\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -4789,40 +4409,12 @@ extension Routing: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBa } extension Routing.Error: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "NONE"), - 1: .same(proto: "NO_ROUTE"), - 2: .same(proto: "GOT_NAK"), - 3: .same(proto: "TIMEOUT"), - 4: .same(proto: "NO_INTERFACE"), - 5: .same(proto: "MAX_RETRANSMIT"), - 6: .same(proto: "NO_CHANNEL"), - 7: .same(proto: "TOO_LARGE"), - 8: .same(proto: "NO_RESPONSE"), - 9: .same(proto: "DUTY_CYCLE_LIMIT"), - 32: .same(proto: "BAD_REQUEST"), - 33: .same(proto: "NOT_AUTHORIZED"), - 34: .same(proto: "PKI_FAILED"), - 35: .same(proto: "PKI_UNKNOWN_PUBKEY"), - 36: .same(proto: "ADMIN_BAD_SESSION_KEY"), - 37: .same(proto: "ADMIN_PUBLIC_KEY_UNAUTHORIZED"), - 38: .same(proto: "RATE_LIMIT_EXCEEDED"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0NONE\0\u{1}NO_ROUTE\0\u{1}GOT_NAK\0\u{1}TIMEOUT\0\u{1}NO_INTERFACE\0\u{1}MAX_RETRANSMIT\0\u{1}NO_CHANNEL\0\u{1}TOO_LARGE\0\u{1}NO_RESPONSE\0\u{1}DUTY_CYCLE_LIMIT\0\u{2}\u{17}BAD_REQUEST\0\u{1}NOT_AUTHORIZED\0\u{1}PKI_FAILED\0\u{1}PKI_UNKNOWN_PUBKEY\0\u{1}ADMIN_BAD_SESSION_KEY\0\u{1}ADMIN_PUBLIC_KEY_UNAUTHORIZED\0\u{1}RATE_LIMIT_EXCEEDED\0") } extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Data" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "portnum"), - 2: .same(proto: "payload"), - 3: .standard(proto: "want_response"), - 4: .same(proto: "dest"), - 5: .same(proto: "source"), - 6: .standard(proto: "request_id"), - 7: .standard(proto: "reply_id"), - 8: .same(proto: "emoji"), - 9: .same(proto: "bitfield"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}portnum\0\u{1}payload\0\u{3}want_response\0\u{1}dest\0\u{1}source\0\u{3}request_id\0\u{3}reply_id\0\u{1}emoji\0\u{1}bitfield\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -4896,11 +4488,7 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati extension KeyVerification: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".KeyVerification" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "nonce"), - 2: .same(proto: "hash1"), - 3: .same(proto: "hash2"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}nonce\0\u{1}hash1\0\u{1}hash2\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -4938,18 +4526,83 @@ extension KeyVerification: SwiftProtobuf.Message, SwiftProtobuf._MessageImplemen } } +extension StoreForwardPlusPlus: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".StoreForwardPlusPlus" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}sfpp_message_type\0\u{3}message_hash\0\u{3}commit_hash\0\u{3}root_hash\0\u{1}message\0\u{3}encapsulated_id\0\u{3}encapsulated_to\0\u{3}encapsulated_from\0\u{3}encapsulated_rxtime\0") + + public mutating func decodeMessage(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.decodeSingularEnumField(value: &self.sfppMessageType) }() + case 2: try { try decoder.decodeSingularBytesField(value: &self.messageHash) }() + case 3: try { try decoder.decodeSingularBytesField(value: &self.commitHash) }() + case 4: try { try decoder.decodeSingularBytesField(value: &self.rootHash) }() + case 5: try { try decoder.decodeSingularBytesField(value: &self.message) }() + case 6: try { try decoder.decodeSingularUInt32Field(value: &self.encapsulatedID) }() + case 7: try { try decoder.decodeSingularUInt32Field(value: &self.encapsulatedTo) }() + case 8: try { try decoder.decodeSingularUInt32Field(value: &self.encapsulatedFrom) }() + case 9: try { try decoder.decodeSingularUInt32Field(value: &self.encapsulatedRxtime) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if self.sfppMessageType != .canonAnnounce { + try visitor.visitSingularEnumField(value: self.sfppMessageType, fieldNumber: 1) + } + if !self.messageHash.isEmpty { + try visitor.visitSingularBytesField(value: self.messageHash, fieldNumber: 2) + } + if !self.commitHash.isEmpty { + try visitor.visitSingularBytesField(value: self.commitHash, fieldNumber: 3) + } + if !self.rootHash.isEmpty { + try visitor.visitSingularBytesField(value: self.rootHash, fieldNumber: 4) + } + if !self.message.isEmpty { + try visitor.visitSingularBytesField(value: self.message, fieldNumber: 5) + } + if self.encapsulatedID != 0 { + try visitor.visitSingularUInt32Field(value: self.encapsulatedID, fieldNumber: 6) + } + if self.encapsulatedTo != 0 { + try visitor.visitSingularUInt32Field(value: self.encapsulatedTo, fieldNumber: 7) + } + if self.encapsulatedFrom != 0 { + try visitor.visitSingularUInt32Field(value: self.encapsulatedFrom, fieldNumber: 8) + } + if self.encapsulatedRxtime != 0 { + try visitor.visitSingularUInt32Field(value: self.encapsulatedRxtime, fieldNumber: 9) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: StoreForwardPlusPlus, rhs: StoreForwardPlusPlus) -> Bool { + if lhs.sfppMessageType != rhs.sfppMessageType {return false} + if lhs.messageHash != rhs.messageHash {return false} + if lhs.commitHash != rhs.commitHash {return false} + if lhs.rootHash != rhs.rootHash {return false} + if lhs.message != rhs.message {return false} + if lhs.encapsulatedID != rhs.encapsulatedID {return false} + if lhs.encapsulatedTo != rhs.encapsulatedTo {return false} + if lhs.encapsulatedFrom != rhs.encapsulatedFrom {return false} + if lhs.encapsulatedRxtime != rhs.encapsulatedRxtime {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension StoreForwardPlusPlus.SFPP_message_type: SwiftProtobuf._ProtoNameProviding { + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0CANON_ANNOUNCE\0\u{1}CHAIN_QUERY\0\u{2}\u{2}LINK_REQUEST\0\u{1}LINK_PROVIDE\0\u{1}LINK_PROVIDE_FIRSTHALF\0\u{1}LINK_PROVIDE_SECONDHALF\0") +} + extension Waypoint: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Waypoint" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "id"), - 2: .standard(proto: "latitude_i"), - 3: .standard(proto: "longitude_i"), - 4: .same(proto: "expire"), - 5: .standard(proto: "locked_to"), - 6: .same(proto: "name"), - 7: .same(proto: "description"), - 8: .same(proto: "icon"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}id\0\u{3}latitude_i\0\u{3}longitude_i\0\u{1}expire\0\u{3}locked_to\0\u{1}name\0\u{1}description\0\u{1}icon\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -5018,12 +4671,7 @@ extension Waypoint: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB extension MqttClientProxyMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".MqttClientProxyMessage" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "topic"), - 2: .same(proto: "data"), - 3: .same(proto: "text"), - 4: .same(proto: "retained"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}topic\0\u{1}data\0\u{1}text\0\u{1}retained\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -5090,29 +4738,7 @@ extension MqttClientProxyMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageI extension MeshPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".MeshPacket" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "from"), - 2: .same(proto: "to"), - 3: .same(proto: "channel"), - 4: .same(proto: "decoded"), - 5: .same(proto: "encrypted"), - 6: .same(proto: "id"), - 7: .standard(proto: "rx_time"), - 8: .standard(proto: "rx_snr"), - 9: .standard(proto: "hop_limit"), - 10: .standard(proto: "want_ack"), - 11: .same(proto: "priority"), - 12: .standard(proto: "rx_rssi"), - 13: .same(proto: "delayed"), - 14: .standard(proto: "via_mqtt"), - 15: .standard(proto: "hop_start"), - 16: .standard(proto: "public_key"), - 17: .standard(proto: "pki_encrypted"), - 18: .standard(proto: "next_hop"), - 19: .standard(proto: "relay_node"), - 20: .standard(proto: "tx_after"), - 21: .standard(proto: "transport_mechanism"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}from\0\u{1}to\0\u{1}channel\0\u{1}decoded\0\u{1}encrypted\0\u{1}id\0\u{3}rx_time\0\u{3}rx_snr\0\u{3}hop_limit\0\u{3}want_ack\0\u{1}priority\0\u{3}rx_rssi\0\u{1}delayed\0\u{3}via_mqtt\0\u{3}hop_start\0\u{3}public_key\0\u{3}pki_encrypted\0\u{3}next_hop\0\u{3}relay_node\0\u{3}tx_after\0\u{3}transport_mechanism\0") fileprivate class _StorageClass { var _from: UInt32 = 0 @@ -5136,15 +4762,11 @@ extension MeshPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio var _txAfter: UInt32 = 0 var _transportMechanism: MeshPacket.TransportMechanism = .transportInternal - #if swift(>=5.10) // This property is used as the initial default value for new instances of the type. // The type itself is protecting the reference to its storage via CoW semantics. // This will force a copy to be made of this reference when the first mutation occurs; // hence, it is safe to mark this as `nonisolated(unsafe)`. static nonisolated(unsafe) let defaultInstance = _StorageClass() - #else - static let defaultInstance = _StorageClass() - #endif private init() {} @@ -5265,7 +4887,7 @@ extension MeshPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio if _storage._rxTime != 0 { try visitor.visitSingularFixed32Field(value: _storage._rxTime, fieldNumber: 7) } - if _storage._rxSnr != 0 { + if _storage._rxSnr.bitPattern != 0 { try visitor.visitSingularFloatField(value: _storage._rxSnr, fieldNumber: 8) } if _storage._hopLimit != 0 { @@ -5346,57 +4968,20 @@ extension MeshPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio } extension MeshPacket.Priority: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "UNSET"), - 1: .same(proto: "MIN"), - 10: .same(proto: "BACKGROUND"), - 64: .same(proto: "DEFAULT"), - 70: .same(proto: "RELIABLE"), - 80: .same(proto: "RESPONSE"), - 100: .same(proto: "HIGH"), - 110: .same(proto: "ALERT"), - 120: .same(proto: "ACK"), - 127: .same(proto: "MAX"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0UNSET\0\u{1}MIN\0\u{2}\u{9}BACKGROUND\0\u{2}6DEFAULT\0\u{2}\u{6}RELIABLE\0\u{2}\u{a}RESPONSE\0\u{2}\u{14}HIGH\0\u{2}\u{a}ALERT\0\u{2}\u{a}ACK\0\u{2}\u{7}MAX\0") } extension MeshPacket.Delayed: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "NO_DELAY"), - 1: .same(proto: "DELAYED_BROADCAST"), - 2: .same(proto: "DELAYED_DIRECT"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0NO_DELAY\0\u{1}DELAYED_BROADCAST\0\u{1}DELAYED_DIRECT\0") } extension MeshPacket.TransportMechanism: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "TRANSPORT_INTERNAL"), - 1: .same(proto: "TRANSPORT_LORA"), - 2: .same(proto: "TRANSPORT_LORA_ALT1"), - 3: .same(proto: "TRANSPORT_LORA_ALT2"), - 4: .same(proto: "TRANSPORT_LORA_ALT3"), - 5: .same(proto: "TRANSPORT_MQTT"), - 6: .same(proto: "TRANSPORT_MULTICAST_UDP"), - 7: .same(proto: "TRANSPORT_API"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0TRANSPORT_INTERNAL\0\u{1}TRANSPORT_LORA\0\u{1}TRANSPORT_LORA_ALT1\0\u{1}TRANSPORT_LORA_ALT2\0\u{1}TRANSPORT_LORA_ALT3\0\u{1}TRANSPORT_MQTT\0\u{1}TRANSPORT_MULTICAST_UDP\0\u{1}TRANSPORT_API\0") } extension NodeInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".NodeInfo" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "num"), - 2: .same(proto: "user"), - 3: .same(proto: "position"), - 4: .same(proto: "snr"), - 5: .standard(proto: "last_heard"), - 6: .standard(proto: "device_metrics"), - 7: .same(proto: "channel"), - 8: .standard(proto: "via_mqtt"), - 9: .standard(proto: "hops_away"), - 10: .standard(proto: "is_favorite"), - 11: .standard(proto: "is_ignored"), - 12: .standard(proto: "is_key_manually_verified"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}num\0\u{1}user\0\u{1}position\0\u{1}snr\0\u{3}last_heard\0\u{3}device_metrics\0\u{1}channel\0\u{3}via_mqtt\0\u{3}hops_away\0\u{3}is_favorite\0\u{3}is_ignored\0\u{3}is_key_manually_verified\0") fileprivate class _StorageClass { var _num: UInt32 = 0 @@ -5412,15 +4997,11 @@ extension NodeInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB var _isIgnored: Bool = false var _isKeyManuallyVerified: Bool = false - #if swift(>=5.10) // This property is used as the initial default value for new instances of the type. // The type itself is protecting the reference to its storage via CoW semantics. // This will force a copy to be made of this reference when the first mutation occurs; // hence, it is safe to mark this as `nonisolated(unsafe)`. static nonisolated(unsafe) let defaultInstance = _StorageClass() - #else - static let defaultInstance = _StorageClass() - #endif private init() {} @@ -5488,7 +5069,7 @@ extension NodeInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB try { if let v = _storage._position { try visitor.visitSingularMessageField(value: v, fieldNumber: 3) } }() - if _storage._snr != 0 { + if _storage._snr.bitPattern != 0 { try visitor.visitSingularFloatField(value: _storage._snr, fieldNumber: 4) } if _storage._lastHeard != 0 { @@ -5547,15 +5128,7 @@ extension NodeInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB extension MyNodeInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".MyNodeInfo" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "my_node_num"), - 8: .standard(proto: "reboot_count"), - 11: .standard(proto: "min_app_version"), - 12: .standard(proto: "device_id"), - 13: .standard(proto: "pio_env"), - 14: .standard(proto: "firmware_edition"), - 15: .standard(proto: "nodedb_count"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}my_node_num\0\u{4}\u{7}reboot_count\0\u{4}\u{3}min_app_version\0\u{3}device_id\0\u{3}pio_env\0\u{3}firmware_edition\0\u{3}nodedb_count\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -5615,12 +5188,7 @@ extension MyNodeInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio extension LogRecord: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".LogRecord" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "message"), - 2: .same(proto: "time"), - 3: .same(proto: "source"), - 4: .same(proto: "level"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}message\0\u{1}time\0\u{1}source\0\u{1}level\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -5664,25 +5232,12 @@ extension LogRecord: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation } extension LogRecord.Level: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "UNSET"), - 5: .same(proto: "TRACE"), - 10: .same(proto: "DEBUG"), - 20: .same(proto: "INFO"), - 30: .same(proto: "WARNING"), - 40: .same(proto: "ERROR"), - 50: .same(proto: "CRITICAL"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0UNSET\0\u{2}\u{5}TRACE\0\u{2}\u{5}DEBUG\0\u{2}\u{a}INFO\0\u{2}\u{a}WARNING\0\u{2}\u{a}ERROR\0\u{2}\u{a}CRITICAL\0") } extension QueueStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".QueueStatus" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "res"), - 2: .same(proto: "free"), - 3: .same(proto: "maxlen"), - 4: .standard(proto: "mesh_packet_id"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}res\0\u{1}free\0\u{1}maxlen\0\u{3}mesh_packet_id\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -5727,25 +5282,7 @@ extension QueueStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati extension FromRadio: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".FromRadio" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "id"), - 2: .same(proto: "packet"), - 3: .standard(proto: "my_info"), - 4: .standard(proto: "node_info"), - 5: .same(proto: "config"), - 6: .standard(proto: "log_record"), - 7: .standard(proto: "config_complete_id"), - 8: .same(proto: "rebooted"), - 9: .same(proto: "moduleConfig"), - 10: .same(proto: "channel"), - 11: .same(proto: "queueStatus"), - 12: .same(proto: "xmodemPacket"), - 13: .same(proto: "metadata"), - 14: .same(proto: "mqttClientProxyMessage"), - 15: .same(proto: "fileInfo"), - 16: .same(proto: "clientNotification"), - 17: .same(proto: "deviceuiConfig"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}id\0\u{1}packet\0\u{3}my_info\0\u{3}node_info\0\u{1}config\0\u{3}log_record\0\u{3}config_complete_id\0\u{1}rebooted\0\u{1}moduleConfig\0\u{1}channel\0\u{1}queueStatus\0\u{1}xmodemPacket\0\u{1}metadata\0\u{1}mqttClientProxyMessage\0\u{1}fileInfo\0\u{1}clientNotification\0\u{1}deviceuiConfig\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -6045,17 +5582,7 @@ extension FromRadio: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation extension ClientNotification: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".ClientNotification" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "reply_id"), - 2: .same(proto: "time"), - 3: .same(proto: "level"), - 4: .same(proto: "message"), - 11: .standard(proto: "key_verification_number_inform"), - 12: .standard(proto: "key_verification_number_request"), - 13: .standard(proto: "key_verification_final"), - 14: .standard(proto: "duplicated_public_key"), - 15: .standard(proto: "low_entropy_key"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}reply_id\0\u{1}time\0\u{1}level\0\u{1}message\0\u{4}\u{7}key_verification_number_inform\0\u{3}key_verification_number_request\0\u{3}key_verification_final\0\u{3}duplicated_public_key\0\u{3}low_entropy_key\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -6193,11 +5720,7 @@ extension ClientNotification: SwiftProtobuf.Message, SwiftProtobuf._MessageImple extension KeyVerificationNumberInform: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".KeyVerificationNumberInform" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "nonce"), - 2: .standard(proto: "remote_longname"), - 3: .standard(proto: "security_number"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}nonce\0\u{3}remote_longname\0\u{3}security_number\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -6237,10 +5760,7 @@ extension KeyVerificationNumberInform: SwiftProtobuf.Message, SwiftProtobuf._Mes extension KeyVerificationNumberRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".KeyVerificationNumberRequest" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "nonce"), - 2: .standard(proto: "remote_longname"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}nonce\0\u{3}remote_longname\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -6275,12 +5795,7 @@ extension KeyVerificationNumberRequest: SwiftProtobuf.Message, SwiftProtobuf._Me extension KeyVerificationFinal: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".KeyVerificationFinal" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "nonce"), - 2: .standard(proto: "remote_longname"), - 3: .same(proto: "isSender"), - 4: .standard(proto: "verification_characters"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}nonce\0\u{3}remote_longname\0\u{1}isSender\0\u{3}verification_characters\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -6328,8 +5843,8 @@ extension DuplicatedPublicKey: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl public static let _protobuf_nameMap = SwiftProtobuf._NameMap() public mutating func decodeMessage(decoder: inout D) throws { - while let _ = try decoder.nextFieldNumber() { - } + // Load everything into unknown fields + while try decoder.nextFieldNumber() != nil {} } public func traverse(visitor: inout V) throws { @@ -6347,8 +5862,8 @@ extension LowEntropyKey: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa public static let _protobuf_nameMap = SwiftProtobuf._NameMap() public mutating func decodeMessage(decoder: inout D) throws { - while let _ = try decoder.nextFieldNumber() { - } + // Load everything into unknown fields + while try decoder.nextFieldNumber() != nil {} } public func traverse(visitor: inout V) throws { @@ -6363,10 +5878,7 @@ extension LowEntropyKey: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa extension FileInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".FileInfo" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "file_name"), - 2: .standard(proto: "size_bytes"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}file_name\0\u{3}size_bytes\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -6401,14 +5913,7 @@ extension FileInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB extension ToRadio: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".ToRadio" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "packet"), - 3: .standard(proto: "want_config_id"), - 4: .same(proto: "disconnect"), - 5: .same(proto: "xmodemPacket"), - 6: .same(proto: "mqttClientProxyMessage"), - 7: .same(proto: "heartbeat"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}packet\0\u{4}\u{2}want_config_id\0\u{1}disconnect\0\u{1}xmodemPacket\0\u{1}mqttClientProxyMessage\0\u{1}heartbeat\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -6533,10 +6038,7 @@ extension ToRadio: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBa extension Compressed: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Compressed" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "portnum"), - 2: .same(proto: "data"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}portnum\0\u{1}data\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -6571,12 +6073,7 @@ extension Compressed: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio extension NeighborInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".NeighborInfo" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "node_id"), - 2: .standard(proto: "last_sent_by_id"), - 3: .standard(proto: "node_broadcast_interval_secs"), - 4: .same(proto: "neighbors"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}node_id\0\u{3}last_sent_by_id\0\u{3}node_broadcast_interval_secs\0\u{1}neighbors\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -6621,12 +6118,7 @@ extension NeighborInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat extension Neighbor: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Neighbor" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "node_id"), - 2: .same(proto: "snr"), - 3: .standard(proto: "last_rx_time"), - 4: .standard(proto: "node_broadcast_interval_secs"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}node_id\0\u{1}snr\0\u{3}last_rx_time\0\u{3}node_broadcast_interval_secs\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -6647,7 +6139,7 @@ extension Neighbor: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB if self.nodeID != 0 { try visitor.visitSingularUInt32Field(value: self.nodeID, fieldNumber: 1) } - if self.snr != 0 { + if self.snr.bitPattern != 0 { try visitor.visitSingularFloatField(value: self.snr, fieldNumber: 2) } if self.lastRxTime != 0 { @@ -6671,20 +6163,7 @@ extension Neighbor: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB extension DeviceMetadata: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".DeviceMetadata" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "firmware_version"), - 2: .standard(proto: "device_state_version"), - 3: .same(proto: "canShutdown"), - 4: .same(proto: "hasWifi"), - 5: .same(proto: "hasBluetooth"), - 6: .same(proto: "hasEthernet"), - 7: .same(proto: "role"), - 8: .standard(proto: "position_flags"), - 9: .standard(proto: "hw_model"), - 10: .same(proto: "hasRemoteHardware"), - 11: .same(proto: "hasPKC"), - 12: .standard(proto: "excluded_modules"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}firmware_version\0\u{3}device_state_version\0\u{1}canShutdown\0\u{1}hasWifi\0\u{1}hasBluetooth\0\u{1}hasEthernet\0\u{1}role\0\u{3}position_flags\0\u{3}hw_model\0\u{1}hasRemoteHardware\0\u{1}hasPKC\0\u{3}excluded_modules\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -6769,9 +6248,7 @@ extension DeviceMetadata: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement extension Heartbeat: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Heartbeat" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "nonce"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}nonce\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -6801,10 +6278,7 @@ extension Heartbeat: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation extension NodeRemoteHardwarePin: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".NodeRemoteHardwarePin" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "node_num"), - 2: .same(proto: "pin"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}node_num\0\u{1}pin\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -6843,12 +6317,7 @@ extension NodeRemoteHardwarePin: SwiftProtobuf.Message, SwiftProtobuf._MessageIm extension ChunkedPayload: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".ChunkedPayload" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "payload_id"), - 2: .standard(proto: "chunk_count"), - 3: .standard(proto: "chunk_index"), - 4: .standard(proto: "payload_chunk"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}payload_id\0\u{3}chunk_count\0\u{3}chunk_index\0\u{3}payload_chunk\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -6893,9 +6362,7 @@ extension ChunkedPayload: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement extension resend_chunks: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".resend_chunks" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "chunks"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}chunks\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -6925,12 +6392,7 @@ extension resend_chunks: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa extension ChunkedPayloadResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".ChunkedPayloadResponse" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "payload_id"), - 2: .standard(proto: "request_transfer"), - 3: .standard(proto: "accept_transfer"), - 4: .standard(proto: "resend_chunks"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}payload_id\0\u{3}request_transfer\0\u{3}accept_transfer\0\u{3}resend_chunks\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { diff --git a/MeshtasticProtobufs/Sources/meshtastic/module_config.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/module_config.pb.swift index 5bf37457..1bff36a2 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/module_config.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/module_config.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/module_config.proto @@ -7,7 +8,6 @@ // 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 @@ -20,7 +20,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP typealias Version = _2 } -public enum RemoteHardwarePinType: SwiftProtobuf.Enum { +public enum RemoteHardwarePinType: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -58,24 +58,18 @@ public enum RemoteHardwarePinType: SwiftProtobuf.Enum { } } -} - -#if swift(>=4.2) - -extension RemoteHardwarePinType: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [RemoteHardwarePinType] = [ .unknown, .digitalRead, .digitalWrite, ] -} -#endif // swift(>=4.2) +} /// /// Module Config -public struct ModuleConfig { +public struct ModuleConfig: Sendable { // 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. @@ -218,7 +212,7 @@ public struct ModuleConfig { /// /// TODO: REPLACE - public enum OneOf_PayloadVariant: Equatable { + public enum OneOf_PayloadVariant: Equatable, Sendable { /// /// TODO: REPLACE case mqtt(ModuleConfig.MQTTConfig) @@ -259,73 +253,11 @@ public struct ModuleConfig { /// TODO: REPLACE case paxcounter(ModuleConfig.PaxcounterConfig) - #if !swift(>=4.1) - public static func ==(lhs: ModuleConfig.OneOf_PayloadVariant, rhs: ModuleConfig.OneOf_PayloadVariant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.mqtt, .mqtt): return { - guard case .mqtt(let l) = lhs, case .mqtt(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.serial, .serial): return { - guard case .serial(let l) = lhs, case .serial(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.externalNotification, .externalNotification): return { - guard case .externalNotification(let l) = lhs, case .externalNotification(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.storeForward, .storeForward): return { - guard case .storeForward(let l) = lhs, case .storeForward(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.rangeTest, .rangeTest): return { - guard case .rangeTest(let l) = lhs, case .rangeTest(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.telemetry, .telemetry): return { - guard case .telemetry(let l) = lhs, case .telemetry(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.cannedMessage, .cannedMessage): return { - guard case .cannedMessage(let l) = lhs, case .cannedMessage(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.audio, .audio): return { - guard case .audio(let l) = lhs, case .audio(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.remoteHardware, .remoteHardware): return { - guard case .remoteHardware(let l) = lhs, case .remoteHardware(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.neighborInfo, .neighborInfo): return { - guard case .neighborInfo(let l) = lhs, case .neighborInfo(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.ambientLighting, .ambientLighting): return { - guard case .ambientLighting(let l) = lhs, case .ambientLighting(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.detectionSensor, .detectionSensor): return { - guard case .detectionSensor(let l) = lhs, case .detectionSensor(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.paxcounter, .paxcounter): return { - guard case .paxcounter(let l) = lhs, case .paxcounter(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } /// /// MQTT Client Config - public struct MQTTConfig { + public struct MQTTConfig: Sendable { // 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. @@ -400,7 +332,7 @@ public struct ModuleConfig { /// /// Settings for reporting unencrypted information about our node to a map via MQTT - public struct MapReportSettings { + public struct MapReportSettings: Sendable { // 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. @@ -424,7 +356,7 @@ public struct ModuleConfig { /// /// RemoteHardwareModule Config - public struct RemoteHardwareConfig { + public struct RemoteHardwareConfig: Sendable { // 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. @@ -448,7 +380,7 @@ public struct ModuleConfig { /// /// NeighborInfoModule Config - public struct NeighborInfoConfig { + public struct NeighborInfoConfig: Sendable { // 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. @@ -474,7 +406,7 @@ public struct ModuleConfig { /// /// Detection Sensor Module Config - public struct DetectionSensorConfig { + public struct DetectionSensorConfig: Sendable { // 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. @@ -521,7 +453,7 @@ public struct ModuleConfig { public var unknownFields = SwiftProtobuf.UnknownStorage() - public enum TriggerType: SwiftProtobuf.Enum { + public enum TriggerType: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// Event is triggered if pin is low @@ -573,6 +505,16 @@ public struct ModuleConfig { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [ModuleConfig.DetectionSensorConfig.TriggerType] = [ + .logicLow, + .logicHigh, + .fallingEdge, + .risingEdge, + .eitherEdgeActiveLow, + .eitherEdgeActiveHigh, + ] + } public init() {} @@ -580,7 +522,7 @@ public struct ModuleConfig { /// /// Audio Config for codec2 voice - public struct AudioConfig { + public struct AudioConfig: Sendable { // 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. @@ -617,7 +559,7 @@ public struct ModuleConfig { /// /// Baudrate for codec2 voice - public enum Audio_Baud: SwiftProtobuf.Enum { + public enum Audio_Baud: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int case codec2Default // = 0 case codec23200 // = 1 @@ -664,6 +606,19 @@ public struct ModuleConfig { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [ModuleConfig.AudioConfig.Audio_Baud] = [ + .codec2Default, + .codec23200, + .codec22400, + .codec21600, + .codec21400, + .codec21300, + .codec21200, + .codec2700, + .codec2700B, + ] + } public init() {} @@ -671,7 +626,7 @@ public struct ModuleConfig { /// /// Config for the Paxcounter Module - public struct PaxcounterConfig { + public struct PaxcounterConfig: Sendable { // 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. @@ -697,7 +652,7 @@ public struct ModuleConfig { /// /// Serial Config - public struct SerialConfig { + public struct SerialConfig: Sendable { // 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. @@ -740,7 +695,7 @@ public struct ModuleConfig { /// /// TODO: REPLACE - public enum Serial_Baud: SwiftProtobuf.Enum { + public enum Serial_Baud: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int case baudDefault // = 0 case baud110 // = 1 @@ -808,11 +763,31 @@ public struct ModuleConfig { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [ModuleConfig.SerialConfig.Serial_Baud] = [ + .baudDefault, + .baud110, + .baud300, + .baud600, + .baud1200, + .baud2400, + .baud4800, + .baud9600, + .baud19200, + .baud38400, + .baud57600, + .baud115200, + .baud230400, + .baud460800, + .baud576000, + .baud921600, + ] + } /// /// TODO: REPLACE - public enum Serial_Mode: SwiftProtobuf.Enum { + public enum Serial_Mode: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int case `default` // = 0 case simple // = 1 @@ -869,6 +844,19 @@ public struct ModuleConfig { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [ModuleConfig.SerialConfig.Serial_Mode] = [ + .default, + .simple, + .proto, + .textmsg, + .nmea, + .caltopo, + .ws85, + .veDirect, + .msConfig, + ] + } public init() {} @@ -876,7 +864,7 @@ public struct ModuleConfig { /// /// External Notifications Config - public struct ExternalNotificationConfig { + public struct ExternalNotificationConfig: Sendable { // 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. @@ -959,7 +947,7 @@ public struct ModuleConfig { /// /// Store and Forward Module Config - public struct StoreForwardConfig { + public struct StoreForwardConfig: Sendable { // 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. @@ -995,7 +983,7 @@ public struct ModuleConfig { /// /// Preferences for the RangeTestModule - public struct RangeTestConfig { + public struct RangeTestConfig: Sendable { // 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. @@ -1025,7 +1013,7 @@ public struct ModuleConfig { /// /// Configuration for both device and environment metrics - public struct TelemetryConfig { + public struct TelemetryConfig: Sendable { // 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. @@ -1099,7 +1087,7 @@ public struct ModuleConfig { /// /// Canned Messages Module Config - public struct CannedMessageConfig { + public struct CannedMessageConfig: Sendable { // 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. @@ -1138,11 +1126,15 @@ public struct ModuleConfig { /// /// Enable/disable CannedMessageModule. + /// + /// NOTE: This field was marked as deprecated in the .proto file. public var enabled: Bool = false /// /// Input event origin accepted by the canned message module. /// Can be e.g. "rotEnc1", "upDownEnc1", "scanAndSelect", "cardkb", "serialkb", or keyword "_any" + /// + /// NOTE: This field was marked as deprecated in the .proto file. public var allowInputSource: String = String() /// @@ -1154,7 +1146,7 @@ public struct ModuleConfig { /// /// TODO: REPLACE - public enum InputEventChar: SwiftProtobuf.Enum { + public enum InputEventChar: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -1222,6 +1214,18 @@ public struct ModuleConfig { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [ModuleConfig.CannedMessageConfig.InputEventChar] = [ + .none, + .up, + .down, + .left, + .right, + .select, + .back, + .cancel, + ] + } public init() {} @@ -1230,7 +1234,7 @@ public struct ModuleConfig { /// ///Ambient Lighting Module - Settings for control of onboard LEDs to allow users to adjust the brightness levels and respective color levels. ///Initially created for the RAK14001 RGB LED module. - public struct AmbientLightingConfig { + public struct AmbientLightingConfig: Sendable { // 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. @@ -1263,91 +1267,9 @@ public struct ModuleConfig { public init() {} } -#if swift(>=4.2) - -extension ModuleConfig.DetectionSensorConfig.TriggerType: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [ModuleConfig.DetectionSensorConfig.TriggerType] = [ - .logicLow, - .logicHigh, - .fallingEdge, - .risingEdge, - .eitherEdgeActiveLow, - .eitherEdgeActiveHigh, - ] -} - -extension ModuleConfig.AudioConfig.Audio_Baud: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [ModuleConfig.AudioConfig.Audio_Baud] = [ - .codec2Default, - .codec23200, - .codec22400, - .codec21600, - .codec21400, - .codec21300, - .codec21200, - .codec2700, - .codec2700B, - ] -} - -extension ModuleConfig.SerialConfig.Serial_Baud: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [ModuleConfig.SerialConfig.Serial_Baud] = [ - .baudDefault, - .baud110, - .baud300, - .baud600, - .baud1200, - .baud2400, - .baud4800, - .baud9600, - .baud19200, - .baud38400, - .baud57600, - .baud115200, - .baud230400, - .baud460800, - .baud576000, - .baud921600, - ] -} - -extension ModuleConfig.SerialConfig.Serial_Mode: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [ModuleConfig.SerialConfig.Serial_Mode] = [ - .default, - .simple, - .proto, - .textmsg, - .nmea, - .caltopo, - .ws85, - .veDirect, - .msConfig, - ] -} - -extension ModuleConfig.CannedMessageConfig.InputEventChar: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [ModuleConfig.CannedMessageConfig.InputEventChar] = [ - .none, - .up, - .down, - .left, - .right, - .select, - .back, - .cancel, - ] -} - -#endif // swift(>=4.2) - /// /// A GPIO pin definition for remote hardware module -public struct RemoteHardwarePin { +public struct RemoteHardwarePin: Sendable { // 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. @@ -1369,61 +1291,17 @@ public struct RemoteHardwarePin { public init() {} } -#if swift(>=5.5) && canImport(_Concurrency) -extension RemoteHardwarePinType: @unchecked Sendable {} -extension ModuleConfig: @unchecked Sendable {} -extension ModuleConfig.OneOf_PayloadVariant: @unchecked Sendable {} -extension ModuleConfig.MQTTConfig: @unchecked Sendable {} -extension ModuleConfig.MapReportSettings: @unchecked Sendable {} -extension ModuleConfig.RemoteHardwareConfig: @unchecked Sendable {} -extension ModuleConfig.NeighborInfoConfig: @unchecked Sendable {} -extension ModuleConfig.DetectionSensorConfig: @unchecked Sendable {} -extension ModuleConfig.DetectionSensorConfig.TriggerType: @unchecked Sendable {} -extension ModuleConfig.AudioConfig: @unchecked Sendable {} -extension ModuleConfig.AudioConfig.Audio_Baud: @unchecked Sendable {} -extension ModuleConfig.PaxcounterConfig: @unchecked Sendable {} -extension ModuleConfig.SerialConfig: @unchecked Sendable {} -extension ModuleConfig.SerialConfig.Serial_Baud: @unchecked Sendable {} -extension ModuleConfig.SerialConfig.Serial_Mode: @unchecked Sendable {} -extension ModuleConfig.ExternalNotificationConfig: @unchecked Sendable {} -extension ModuleConfig.StoreForwardConfig: @unchecked Sendable {} -extension ModuleConfig.RangeTestConfig: @unchecked Sendable {} -extension ModuleConfig.TelemetryConfig: @unchecked Sendable {} -extension ModuleConfig.CannedMessageConfig: @unchecked Sendable {} -extension ModuleConfig.CannedMessageConfig.InputEventChar: @unchecked Sendable {} -extension ModuleConfig.AmbientLightingConfig: @unchecked Sendable {} -extension RemoteHardwarePin: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" extension RemoteHardwarePinType: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "UNKNOWN"), - 1: .same(proto: "DIGITAL_READ"), - 2: .same(proto: "DIGITAL_WRITE"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0UNKNOWN\0\u{1}DIGITAL_READ\0\u{1}DIGITAL_WRITE\0") } extension ModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".ModuleConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "mqtt"), - 2: .same(proto: "serial"), - 3: .standard(proto: "external_notification"), - 4: .standard(proto: "store_forward"), - 5: .standard(proto: "range_test"), - 6: .same(proto: "telemetry"), - 7: .standard(proto: "canned_message"), - 8: .same(proto: "audio"), - 9: .standard(proto: "remote_hardware"), - 10: .standard(proto: "neighbor_info"), - 11: .standard(proto: "ambient_lighting"), - 12: .standard(proto: "detection_sensor"), - 13: .same(proto: "paxcounter"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}mqtt\0\u{1}serial\0\u{3}external_notification\0\u{3}store_forward\0\u{3}range_test\0\u{1}telemetry\0\u{3}canned_message\0\u{1}audio\0\u{3}remote_hardware\0\u{3}neighbor_info\0\u{3}ambient_lighting\0\u{3}detection_sensor\0\u{1}paxcounter\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -1677,19 +1555,7 @@ extension ModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = ModuleConfig.protoMessageName + ".MQTTConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "enabled"), - 2: .same(proto: "address"), - 3: .same(proto: "username"), - 4: .same(proto: "password"), - 5: .standard(proto: "encryption_enabled"), - 6: .standard(proto: "json_enabled"), - 7: .standard(proto: "tls_enabled"), - 8: .same(proto: "root"), - 9: .standard(proto: "proxy_to_client_enabled"), - 10: .standard(proto: "map_reporting_enabled"), - 11: .standard(proto: "map_report_settings"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}enabled\0\u{1}address\0\u{1}username\0\u{1}password\0\u{3}encryption_enabled\0\u{3}json_enabled\0\u{3}tls_enabled\0\u{1}root\0\u{3}proxy_to_client_enabled\0\u{3}map_reporting_enabled\0\u{3}map_report_settings\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -1773,11 +1639,7 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message extension ModuleConfig.MapReportSettings: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = ModuleConfig.protoMessageName + ".MapReportSettings" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "publish_interval_secs"), - 2: .standard(proto: "position_precision"), - 3: .standard(proto: "should_report_location"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}publish_interval_secs\0\u{3}position_precision\0\u{3}should_report_location\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -1817,11 +1679,7 @@ extension ModuleConfig.MapReportSettings: SwiftProtobuf.Message, SwiftProtobuf._ extension ModuleConfig.RemoteHardwareConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = ModuleConfig.protoMessageName + ".RemoteHardwareConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "enabled"), - 2: .standard(proto: "allow_undefined_pin_access"), - 3: .standard(proto: "available_pins"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}enabled\0\u{3}allow_undefined_pin_access\0\u{3}available_pins\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -1861,11 +1719,7 @@ extension ModuleConfig.RemoteHardwareConfig: SwiftProtobuf.Message, SwiftProtobu extension ModuleConfig.NeighborInfoConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = ModuleConfig.protoMessageName + ".NeighborInfoConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "enabled"), - 2: .standard(proto: "update_interval"), - 3: .standard(proto: "transmit_over_lora"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}enabled\0\u{3}update_interval\0\u{3}transmit_over_lora\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -1905,16 +1759,7 @@ extension ModuleConfig.NeighborInfoConfig: SwiftProtobuf.Message, SwiftProtobuf. extension ModuleConfig.DetectionSensorConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = ModuleConfig.protoMessageName + ".DetectionSensorConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "enabled"), - 2: .standard(proto: "minimum_broadcast_secs"), - 3: .standard(proto: "state_broadcast_secs"), - 4: .standard(proto: "send_bell"), - 5: .same(proto: "name"), - 6: .standard(proto: "monitor_pin"), - 7: .standard(proto: "detection_trigger_type"), - 8: .standard(proto: "use_pullup"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}enabled\0\u{3}minimum_broadcast_secs\0\u{3}state_broadcast_secs\0\u{3}send_bell\0\u{1}name\0\u{3}monitor_pin\0\u{3}detection_trigger_type\0\u{3}use_pullup\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -1978,27 +1823,12 @@ extension ModuleConfig.DetectionSensorConfig: SwiftProtobuf.Message, SwiftProtob } extension ModuleConfig.DetectionSensorConfig.TriggerType: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "LOGIC_LOW"), - 1: .same(proto: "LOGIC_HIGH"), - 2: .same(proto: "FALLING_EDGE"), - 3: .same(proto: "RISING_EDGE"), - 4: .same(proto: "EITHER_EDGE_ACTIVE_LOW"), - 5: .same(proto: "EITHER_EDGE_ACTIVE_HIGH"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0LOGIC_LOW\0\u{1}LOGIC_HIGH\0\u{1}FALLING_EDGE\0\u{1}RISING_EDGE\0\u{1}EITHER_EDGE_ACTIVE_LOW\0\u{1}EITHER_EDGE_ACTIVE_HIGH\0") } extension ModuleConfig.AudioConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = ModuleConfig.protoMessageName + ".AudioConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "codec2_enabled"), - 2: .standard(proto: "ptt_pin"), - 3: .same(proto: "bitrate"), - 4: .standard(proto: "i2s_ws"), - 5: .standard(proto: "i2s_sd"), - 6: .standard(proto: "i2s_din"), - 7: .standard(proto: "i2s_sck"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}codec2_enabled\0\u{3}ptt_pin\0\u{1}bitrate\0\u{3}i2s_ws\0\u{3}i2s_sd\0\u{3}i2s_din\0\u{3}i2s_sck\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2057,27 +1887,12 @@ extension ModuleConfig.AudioConfig: SwiftProtobuf.Message, SwiftProtobuf._Messag } extension ModuleConfig.AudioConfig.Audio_Baud: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "CODEC2_DEFAULT"), - 1: .same(proto: "CODEC2_3200"), - 2: .same(proto: "CODEC2_2400"), - 3: .same(proto: "CODEC2_1600"), - 4: .same(proto: "CODEC2_1400"), - 5: .same(proto: "CODEC2_1300"), - 6: .same(proto: "CODEC2_1200"), - 7: .same(proto: "CODEC2_700"), - 8: .same(proto: "CODEC2_700B"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0CODEC2_DEFAULT\0\u{1}CODEC2_3200\0\u{1}CODEC2_2400\0\u{1}CODEC2_1600\0\u{1}CODEC2_1400\0\u{1}CODEC2_1300\0\u{1}CODEC2_1200\0\u{1}CODEC2_700\0\u{1}CODEC2_700B\0") } extension ModuleConfig.PaxcounterConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = ModuleConfig.protoMessageName + ".PaxcounterConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "enabled"), - 2: .standard(proto: "paxcounter_update_interval"), - 3: .standard(proto: "wifi_threshold"), - 4: .standard(proto: "ble_threshold"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}enabled\0\u{3}paxcounter_update_interval\0\u{3}wifi_threshold\0\u{3}ble_threshold\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2122,16 +1937,7 @@ extension ModuleConfig.PaxcounterConfig: SwiftProtobuf.Message, SwiftProtobuf._M extension ModuleConfig.SerialConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = ModuleConfig.protoMessageName + ".SerialConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "enabled"), - 2: .same(proto: "echo"), - 3: .same(proto: "rxd"), - 4: .same(proto: "txd"), - 5: .same(proto: "baud"), - 6: .same(proto: "timeout"), - 7: .same(proto: "mode"), - 8: .standard(proto: "override_console_serial_port"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}enabled\0\u{1}echo\0\u{1}rxd\0\u{1}txd\0\u{1}baud\0\u{1}timeout\0\u{1}mode\0\u{3}override_console_serial_port\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2195,59 +2001,16 @@ extension ModuleConfig.SerialConfig: SwiftProtobuf.Message, SwiftProtobuf._Messa } extension ModuleConfig.SerialConfig.Serial_Baud: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "BAUD_DEFAULT"), - 1: .same(proto: "BAUD_110"), - 2: .same(proto: "BAUD_300"), - 3: .same(proto: "BAUD_600"), - 4: .same(proto: "BAUD_1200"), - 5: .same(proto: "BAUD_2400"), - 6: .same(proto: "BAUD_4800"), - 7: .same(proto: "BAUD_9600"), - 8: .same(proto: "BAUD_19200"), - 9: .same(proto: "BAUD_38400"), - 10: .same(proto: "BAUD_57600"), - 11: .same(proto: "BAUD_115200"), - 12: .same(proto: "BAUD_230400"), - 13: .same(proto: "BAUD_460800"), - 14: .same(proto: "BAUD_576000"), - 15: .same(proto: "BAUD_921600"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0BAUD_DEFAULT\0\u{1}BAUD_110\0\u{1}BAUD_300\0\u{1}BAUD_600\0\u{1}BAUD_1200\0\u{1}BAUD_2400\0\u{1}BAUD_4800\0\u{1}BAUD_9600\0\u{1}BAUD_19200\0\u{1}BAUD_38400\0\u{1}BAUD_57600\0\u{1}BAUD_115200\0\u{1}BAUD_230400\0\u{1}BAUD_460800\0\u{1}BAUD_576000\0\u{1}BAUD_921600\0") } extension ModuleConfig.SerialConfig.Serial_Mode: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "DEFAULT"), - 1: .same(proto: "SIMPLE"), - 2: .same(proto: "PROTO"), - 3: .same(proto: "TEXTMSG"), - 4: .same(proto: "NMEA"), - 5: .same(proto: "CALTOPO"), - 6: .same(proto: "WS85"), - 7: .same(proto: "VE_DIRECT"), - 8: .same(proto: "MS_CONFIG"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0DEFAULT\0\u{1}SIMPLE\0\u{1}PROTO\0\u{1}TEXTMSG\0\u{1}NMEA\0\u{1}CALTOPO\0\u{1}WS85\0\u{1}VE_DIRECT\0\u{1}MS_CONFIG\0") } extension ModuleConfig.ExternalNotificationConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = ModuleConfig.protoMessageName + ".ExternalNotificationConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "enabled"), - 2: .standard(proto: "output_ms"), - 3: .same(proto: "output"), - 8: .standard(proto: "output_vibra"), - 9: .standard(proto: "output_buzzer"), - 4: .same(proto: "active"), - 5: .standard(proto: "alert_message"), - 10: .standard(proto: "alert_message_vibra"), - 11: .standard(proto: "alert_message_buzzer"), - 6: .standard(proto: "alert_bell"), - 12: .standard(proto: "alert_bell_vibra"), - 13: .standard(proto: "alert_bell_buzzer"), - 7: .standard(proto: "use_pwm"), - 14: .standard(proto: "nag_timeout"), - 15: .standard(proto: "use_i2s_as_buzzer"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}enabled\0\u{3}output_ms\0\u{1}output\0\u{1}active\0\u{3}alert_message\0\u{3}alert_bell\0\u{3}use_pwm\0\u{3}output_vibra\0\u{3}output_buzzer\0\u{3}alert_message_vibra\0\u{3}alert_message_buzzer\0\u{3}alert_bell_vibra\0\u{3}alert_bell_buzzer\0\u{3}nag_timeout\0\u{3}use_i2s_as_buzzer\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2347,14 +2110,7 @@ extension ModuleConfig.ExternalNotificationConfig: SwiftProtobuf.Message, SwiftP extension ModuleConfig.StoreForwardConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = ModuleConfig.protoMessageName + ".StoreForwardConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "enabled"), - 2: .same(proto: "heartbeat"), - 3: .same(proto: "records"), - 4: .standard(proto: "history_return_max"), - 5: .standard(proto: "history_return_window"), - 6: .standard(proto: "is_server"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}enabled\0\u{1}heartbeat\0\u{1}records\0\u{3}history_return_max\0\u{3}history_return_window\0\u{3}is_server\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2409,12 +2165,7 @@ extension ModuleConfig.StoreForwardConfig: SwiftProtobuf.Message, SwiftProtobuf. extension ModuleConfig.RangeTestConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = ModuleConfig.protoMessageName + ".RangeTestConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "enabled"), - 2: .same(proto: "sender"), - 3: .same(proto: "save"), - 4: .standard(proto: "clear_on_reboot"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}enabled\0\u{1}sender\0\u{1}save\0\u{3}clear_on_reboot\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2459,22 +2210,7 @@ extension ModuleConfig.RangeTestConfig: SwiftProtobuf.Message, SwiftProtobuf._Me extension ModuleConfig.TelemetryConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = ModuleConfig.protoMessageName + ".TelemetryConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "device_update_interval"), - 2: .standard(proto: "environment_update_interval"), - 3: .standard(proto: "environment_measurement_enabled"), - 4: .standard(proto: "environment_screen_enabled"), - 5: .standard(proto: "environment_display_fahrenheit"), - 6: .standard(proto: "air_quality_enabled"), - 7: .standard(proto: "air_quality_interval"), - 8: .standard(proto: "power_measurement_enabled"), - 9: .standard(proto: "power_update_interval"), - 10: .standard(proto: "power_screen_enabled"), - 11: .standard(proto: "health_measurement_enabled"), - 12: .standard(proto: "health_update_interval"), - 13: .standard(proto: "health_screen_enabled"), - 14: .standard(proto: "device_telemetry_enabled"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}device_update_interval\0\u{3}environment_update_interval\0\u{3}environment_measurement_enabled\0\u{3}environment_screen_enabled\0\u{3}environment_display_fahrenheit\0\u{3}air_quality_enabled\0\u{3}air_quality_interval\0\u{3}power_measurement_enabled\0\u{3}power_update_interval\0\u{3}power_screen_enabled\0\u{3}health_measurement_enabled\0\u{3}health_update_interval\0\u{3}health_screen_enabled\0\u{3}device_telemetry_enabled\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2569,19 +2305,7 @@ extension ModuleConfig.TelemetryConfig: SwiftProtobuf.Message, SwiftProtobuf._Me extension ModuleConfig.CannedMessageConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = ModuleConfig.protoMessageName + ".CannedMessageConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "rotary1_enabled"), - 2: .standard(proto: "inputbroker_pin_a"), - 3: .standard(proto: "inputbroker_pin_b"), - 4: .standard(proto: "inputbroker_pin_press"), - 5: .standard(proto: "inputbroker_event_cw"), - 6: .standard(proto: "inputbroker_event_ccw"), - 7: .standard(proto: "inputbroker_event_press"), - 8: .standard(proto: "updown1_enabled"), - 9: .same(proto: "enabled"), - 10: .standard(proto: "allow_input_source"), - 11: .standard(proto: "send_bell"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}rotary1_enabled\0\u{3}inputbroker_pin_a\0\u{3}inputbroker_pin_b\0\u{3}inputbroker_pin_press\0\u{3}inputbroker_event_cw\0\u{3}inputbroker_event_ccw\0\u{3}inputbroker_event_press\0\u{3}updown1_enabled\0\u{1}enabled\0\u{3}allow_input_source\0\u{3}send_bell\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2660,27 +2384,12 @@ extension ModuleConfig.CannedMessageConfig: SwiftProtobuf.Message, SwiftProtobuf } extension ModuleConfig.CannedMessageConfig.InputEventChar: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "NONE"), - 10: .same(proto: "SELECT"), - 17: .same(proto: "UP"), - 18: .same(proto: "DOWN"), - 19: .same(proto: "LEFT"), - 20: .same(proto: "RIGHT"), - 24: .same(proto: "CANCEL"), - 27: .same(proto: "BACK"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0NONE\0\u{2}\u{a}SELECT\0\u{2}\u{7}UP\0\u{1}DOWN\0\u{1}LEFT\0\u{1}RIGHT\0\u{2}\u{4}CANCEL\0\u{2}\u{3}BACK\0") } extension ModuleConfig.AmbientLightingConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = ModuleConfig.protoMessageName + ".AmbientLightingConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "led_state"), - 2: .same(proto: "current"), - 3: .same(proto: "red"), - 4: .same(proto: "green"), - 5: .same(proto: "blue"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}led_state\0\u{1}current\0\u{1}red\0\u{1}green\0\u{1}blue\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2730,11 +2439,7 @@ extension ModuleConfig.AmbientLightingConfig: SwiftProtobuf.Message, SwiftProtob extension RemoteHardwarePin: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".RemoteHardwarePin" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "gpio_pin"), - 2: .same(proto: "name"), - 3: .same(proto: "type"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}gpio_pin\0\u{1}name\0\u{1}type\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { diff --git a/MeshtasticProtobufs/Sources/meshtastic/mqtt.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/mqtt.pb.swift index 94ed8cc9..4415e19a 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/mqtt.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/mqtt.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/mqtt.proto @@ -7,7 +8,6 @@ // 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 @@ -22,7 +22,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// /// This message wraps a MeshPacket with extra metadata about the sender and how it arrived. -public struct ServiceEnvelope { +public struct ServiceEnvelope: Sendable { // 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. @@ -57,7 +57,7 @@ public struct ServiceEnvelope { /// /// Information about a node intended to be reported unencrypted to a map using MQTT. -public struct MapReport { +public struct MapReport: Sendable { // 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. @@ -126,22 +126,13 @@ public struct MapReport { public init() {} } -#if swift(>=5.5) && canImport(_Concurrency) -extension ServiceEnvelope: @unchecked Sendable {} -extension MapReport: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" extension ServiceEnvelope: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".ServiceEnvelope" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "packet"), - 2: .standard(proto: "channel_id"), - 3: .standard(proto: "gateway_id"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}packet\0\u{3}channel_id\0\u{3}gateway_id\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -185,22 +176,7 @@ extension ServiceEnvelope: SwiftProtobuf.Message, SwiftProtobuf._MessageImplemen extension MapReport: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".MapReport" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "long_name"), - 2: .standard(proto: "short_name"), - 3: .same(proto: "role"), - 4: .standard(proto: "hw_model"), - 5: .standard(proto: "firmware_version"), - 6: .same(proto: "region"), - 7: .standard(proto: "modem_preset"), - 8: .standard(proto: "has_default_channel"), - 9: .standard(proto: "latitude_i"), - 10: .standard(proto: "longitude_i"), - 11: .same(proto: "altitude"), - 12: .standard(proto: "position_precision"), - 13: .standard(proto: "num_online_local_nodes"), - 14: .standard(proto: "has_opted_report_location"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}long_name\0\u{3}short_name\0\u{1}role\0\u{3}hw_model\0\u{3}firmware_version\0\u{1}region\0\u{3}modem_preset\0\u{3}has_default_channel\0\u{3}latitude_i\0\u{3}longitude_i\0\u{1}altitude\0\u{3}position_precision\0\u{3}num_online_local_nodes\0\u{3}has_opted_report_location\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { diff --git a/MeshtasticProtobufs/Sources/meshtastic/paxcount.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/paxcount.pb.swift index cf8aa463..0864d08f 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/paxcount.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/paxcount.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/paxcount.proto @@ -7,7 +8,6 @@ // 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 @@ -22,7 +22,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// /// TODO: REPLACE -public struct Paxcount { +public struct Paxcount: Sendable { // 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. @@ -44,21 +44,13 @@ public struct Paxcount { public init() {} } -#if swift(>=5.5) && canImport(_Concurrency) -extension Paxcount: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" extension Paxcount: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Paxcount" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "wifi"), - 2: .same(proto: "ble"), - 3: .same(proto: "uptime"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}wifi\0\u{1}ble\0\u{1}uptime\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { diff --git a/MeshtasticProtobufs/Sources/meshtastic/portnums.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/portnums.pb.swift index f9ff36f6..1d264b5b 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/portnums.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/portnums.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/portnums.proto @@ -7,7 +8,6 @@ // 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 @@ -33,7 +33,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// Note: This was formerly a Type enum named 'typ' with the same id # /// We have change to this 'portnum' based scheme for specifying app handlers for particular payloads. /// This change is backwards compatible by treating the legacy OPAQUE/CLEAR_TEXT values identically. -public enum PortNum: SwiftProtobuf.Enum { +public enum PortNum: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -131,6 +131,13 @@ public enum PortNum: SwiftProtobuf.Enum { /// ENCODING: protobuf case paxcounterApp // = 34 + /// + /// Store and Forward++ module included in the firmware + /// ENCODING: protobuf + /// This module is specifically for Native Linux nodes, and provides a Git-style + /// chain of messages. + case storeForwardPlusplusApp // = 35 + /// /// Provides a hardware serial interface to send and receive from the Meshtastic network. /// Connect to the RX/TX pins of a device with 38400 8N1. Packets received from the Meshtastic @@ -246,6 +253,7 @@ public enum PortNum: SwiftProtobuf.Enum { case 32: self = .replyApp case 33: self = .ipTunnelApp case 34: self = .paxcounterApp + case 35: self = .storeForwardPlusplusApp case 64: self = .serialApp case 65: self = .storeForwardApp case 66: self = .rangeTestApp @@ -284,6 +292,7 @@ public enum PortNum: SwiftProtobuf.Enum { case .replyApp: return 32 case .ipTunnelApp: return 33 case .paxcounterApp: return 34 + case .storeForwardPlusplusApp: return 35 case .serialApp: return 64 case .storeForwardApp: return 65 case .rangeTestApp: return 66 @@ -304,11 +313,6 @@ public enum PortNum: SwiftProtobuf.Enum { } } -} - -#if swift(>=4.2) - -extension PortNum: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [PortNum] = [ .unknownApp, @@ -327,6 +331,7 @@ extension PortNum: CaseIterable { .replyApp, .ipTunnelApp, .paxcounterApp, + .storeForwardPlusplusApp, .serialApp, .storeForwardApp, .rangeTestApp, @@ -344,49 +349,11 @@ extension PortNum: CaseIterable { .atakForwarder, .max, ] + } -#endif // swift(>=4.2) - -#if swift(>=5.5) && canImport(_Concurrency) -extension PortNum: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. extension PortNum: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "UNKNOWN_APP"), - 1: .same(proto: "TEXT_MESSAGE_APP"), - 2: .same(proto: "REMOTE_HARDWARE_APP"), - 3: .same(proto: "POSITION_APP"), - 4: .same(proto: "NODEINFO_APP"), - 5: .same(proto: "ROUTING_APP"), - 6: .same(proto: "ADMIN_APP"), - 7: .same(proto: "TEXT_MESSAGE_COMPRESSED_APP"), - 8: .same(proto: "WAYPOINT_APP"), - 9: .same(proto: "AUDIO_APP"), - 10: .same(proto: "DETECTION_SENSOR_APP"), - 11: .same(proto: "ALERT_APP"), - 12: .same(proto: "KEY_VERIFICATION_APP"), - 32: .same(proto: "REPLY_APP"), - 33: .same(proto: "IP_TUNNEL_APP"), - 34: .same(proto: "PAXCOUNTER_APP"), - 64: .same(proto: "SERIAL_APP"), - 65: .same(proto: "STORE_FORWARD_APP"), - 66: .same(proto: "RANGE_TEST_APP"), - 67: .same(proto: "TELEMETRY_APP"), - 68: .same(proto: "ZPS_APP"), - 69: .same(proto: "SIMULATOR_APP"), - 70: .same(proto: "TRACEROUTE_APP"), - 71: .same(proto: "NEIGHBORINFO_APP"), - 72: .same(proto: "ATAK_PLUGIN"), - 73: .same(proto: "MAP_REPORT_APP"), - 74: .same(proto: "POWERSTRESS_APP"), - 76: .same(proto: "RETICULUM_TUNNEL_APP"), - 77: .same(proto: "CAYENNE_APP"), - 256: .same(proto: "PRIVATE_APP"), - 257: .same(proto: "ATAK_FORWARDER"), - 511: .same(proto: "MAX"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0UNKNOWN_APP\0\u{1}TEXT_MESSAGE_APP\0\u{1}REMOTE_HARDWARE_APP\0\u{1}POSITION_APP\0\u{1}NODEINFO_APP\0\u{1}ROUTING_APP\0\u{1}ADMIN_APP\0\u{1}TEXT_MESSAGE_COMPRESSED_APP\0\u{1}WAYPOINT_APP\0\u{1}AUDIO_APP\0\u{1}DETECTION_SENSOR_APP\0\u{1}ALERT_APP\0\u{1}KEY_VERIFICATION_APP\0\u{2}\u{14}REPLY_APP\0\u{1}IP_TUNNEL_APP\0\u{1}PAXCOUNTER_APP\0\u{1}STORE_FORWARD_PLUSPLUS_APP\0\u{2}\u{1d}SERIAL_APP\0\u{1}STORE_FORWARD_APP\0\u{1}RANGE_TEST_APP\0\u{1}TELEMETRY_APP\0\u{1}ZPS_APP\0\u{1}SIMULATOR_APP\0\u{1}TRACEROUTE_APP\0\u{1}NEIGHBORINFO_APP\0\u{1}ATAK_PLUGIN\0\u{1}MAP_REPORT_APP\0\u{1}POWERSTRESS_APP\0\u{2}\u{2}RETICULUM_TUNNEL_APP\0\u{1}CAYENNE_APP\0\u{2}s\u{2}PRIVATE_APP\0\u{1}ATAK_FORWARDER\0\u{2}~\u{3}MAX\0") } diff --git a/MeshtasticProtobufs/Sources/meshtastic/powermon.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/powermon.pb.swift index 92f3c5ce..7332ba7c 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/powermon.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/powermon.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/powermon.proto @@ -7,7 +8,6 @@ // 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 @@ -22,7 +22,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// Note: There are no 'PowerMon' messages normally in use (PowerMons are sent only as structured logs - slogs). /// But we wrap our State enum in this message to effectively nest a namespace (without our linter yelling at us) -public struct PowerMon { +public struct PowerMon: Sendable { // 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. @@ -31,7 +31,7 @@ public struct PowerMon { /// Any significant power changing event in meshtastic should be tagged with a powermon state transition. /// If you are making new meshtastic features feel free to add new entries at the end of this definition. - public enum State: SwiftProtobuf.Enum { + public enum State: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int case none // = 0 case cpuDeepSleep // = 1 @@ -104,37 +104,31 @@ public struct PowerMon { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [PowerMon.State] = [ + .none, + .cpuDeepSleep, + .cpuLightSleep, + .vext1On, + .loraRxon, + .loraTxon, + .loraRxactive, + .btOn, + .ledOn, + .screenOn, + .screenDrawing, + .wifiOn, + .gpsActive, + ] + } public init() {} } -#if swift(>=4.2) - -extension PowerMon.State: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [PowerMon.State] = [ - .none, - .cpuDeepSleep, - .cpuLightSleep, - .vext1On, - .loraRxon, - .loraTxon, - .loraRxactive, - .btOn, - .ledOn, - .screenOn, - .screenDrawing, - .wifiOn, - .gpsActive, - ] -} - -#endif // swift(>=4.2) - /// /// PowerStress testing support via the C++ PowerStress module -public struct PowerStressMessage { +public struct PowerStressMessage: Sendable { // 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. @@ -151,7 +145,7 @@ public struct PowerStressMessage { /// What operation would we like the UUT to perform. /// note: senders should probably set want_response in their request packets, so that they can know when the state /// machine has started processing their request - public enum Opcode: SwiftProtobuf.Enum { + public enum Opcode: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -272,48 +266,35 @@ public struct PowerStressMessage { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [PowerStressMessage.Opcode] = [ + .unset, + .printInfo, + .forceQuiet, + .endQuiet, + .screenOn, + .screenOff, + .cpuIdle, + .cpuDeepsleep, + .cpuFullon, + .ledOn, + .ledOff, + .loraOff, + .loraTx, + .loraRx, + .btOff, + .btOn, + .wifiOff, + .wifiOn, + .gpsOff, + .gpsOn, + ] + } public init() {} } -#if swift(>=4.2) - -extension PowerStressMessage.Opcode: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [PowerStressMessage.Opcode] = [ - .unset, - .printInfo, - .forceQuiet, - .endQuiet, - .screenOn, - .screenOff, - .cpuIdle, - .cpuDeepsleep, - .cpuFullon, - .ledOn, - .ledOff, - .loraOff, - .loraTx, - .loraRx, - .btOff, - .btOn, - .wifiOff, - .wifiOn, - .gpsOff, - .gpsOn, - ] -} - -#endif // swift(>=4.2) - -#if swift(>=5.5) && canImport(_Concurrency) -extension PowerMon: @unchecked Sendable {} -extension PowerMon.State: @unchecked Sendable {} -extension PowerStressMessage: @unchecked Sendable {} -extension PowerStressMessage.Opcode: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" @@ -323,8 +304,8 @@ extension PowerMon: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB public static let _protobuf_nameMap = SwiftProtobuf._NameMap() public mutating func decodeMessage(decoder: inout D) throws { - while let _ = try decoder.nextFieldNumber() { - } + // Load everything into unknown fields + while try decoder.nextFieldNumber() != nil {} } public func traverse(visitor: inout V) throws { @@ -338,29 +319,12 @@ extension PowerMon: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB } extension PowerMon.State: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "None"), - 1: .same(proto: "CPU_DeepSleep"), - 2: .same(proto: "CPU_LightSleep"), - 4: .same(proto: "Vext1_On"), - 8: .same(proto: "Lora_RXOn"), - 16: .same(proto: "Lora_TXOn"), - 32: .same(proto: "Lora_RXActive"), - 64: .same(proto: "BT_On"), - 128: .same(proto: "LED_On"), - 256: .same(proto: "Screen_On"), - 512: .same(proto: "Screen_Drawing"), - 1024: .same(proto: "Wifi_On"), - 2048: .same(proto: "GPS_Active"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0None\0\u{1}CPU_DeepSleep\0\u{1}CPU_LightSleep\0\u{2}\u{2}Vext1_On\0\u{2}\u{4}Lora_RXOn\0\u{2}\u{8}Lora_TXOn\0\u{2}\u{10}Lora_RXActive\0\u{2} BT_On\0\u{2}@\u{1}LED_On\0\u{2}@\u{2}Screen_On\0\u{2}@\u{4}Screen_Drawing\0\u{2}@\u{8}Wifi_On\0\u{2}@\u{10}GPS_Active\0") } extension PowerStressMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".PowerStressMessage" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "cmd"), - 2: .standard(proto: "num_seconds"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}cmd\0\u{3}num_seconds\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -379,7 +343,7 @@ extension PowerStressMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImple if self.cmd != .unset { try visitor.visitSingularEnumField(value: self.cmd, fieldNumber: 1) } - if self.numSeconds != 0 { + if self.numSeconds.bitPattern != 0 { try visitor.visitSingularFloatField(value: self.numSeconds, fieldNumber: 2) } try unknownFields.traverse(visitor: &visitor) @@ -394,26 +358,5 @@ extension PowerStressMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImple } extension PowerStressMessage.Opcode: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "UNSET"), - 1: .same(proto: "PRINT_INFO"), - 2: .same(proto: "FORCE_QUIET"), - 3: .same(proto: "END_QUIET"), - 16: .same(proto: "SCREEN_ON"), - 17: .same(proto: "SCREEN_OFF"), - 32: .same(proto: "CPU_IDLE"), - 33: .same(proto: "CPU_DEEPSLEEP"), - 34: .same(proto: "CPU_FULLON"), - 48: .same(proto: "LED_ON"), - 49: .same(proto: "LED_OFF"), - 64: .same(proto: "LORA_OFF"), - 65: .same(proto: "LORA_TX"), - 66: .same(proto: "LORA_RX"), - 80: .same(proto: "BT_OFF"), - 81: .same(proto: "BT_ON"), - 96: .same(proto: "WIFI_OFF"), - 97: .same(proto: "WIFI_ON"), - 112: .same(proto: "GPS_OFF"), - 113: .same(proto: "GPS_ON"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0UNSET\0\u{1}PRINT_INFO\0\u{1}FORCE_QUIET\0\u{1}END_QUIET\0\u{2}\u{d}SCREEN_ON\0\u{1}SCREEN_OFF\0\u{2}\u{f}CPU_IDLE\0\u{1}CPU_DEEPSLEEP\0\u{1}CPU_FULLON\0\u{2}\u{e}LED_ON\0\u{1}LED_OFF\0\u{2}\u{f}LORA_OFF\0\u{1}LORA_TX\0\u{1}LORA_RX\0\u{2}\u{e}BT_OFF\0\u{1}BT_ON\0\u{2}\u{f}WIFI_OFF\0\u{1}WIFI_ON\0\u{2}\u{f}GPS_OFF\0\u{1}GPS_ON\0") } diff --git a/MeshtasticProtobufs/Sources/meshtastic/remote_hardware.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/remote_hardware.pb.swift index ac6eeb26..001db067 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/remote_hardware.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/remote_hardware.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/remote_hardware.proto @@ -7,7 +8,6 @@ // 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 @@ -30,7 +30,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// because no security yet (beyond the channel mechanism). /// It should be off by default and then protected based on some TBD mechanism /// (a special channel once multichannel support is included?) -public struct HardwareMessage { +public struct HardwareMessage: Sendable { // 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. @@ -52,7 +52,7 @@ public struct HardwareMessage { /// /// TODO: REPLACE - public enum TypeEnum: SwiftProtobuf.Enum { + public enum TypeEnum: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -110,43 +110,28 @@ public struct HardwareMessage { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [HardwareMessage.TypeEnum] = [ + .unset, + .writeGpios, + .watchGpios, + .gpiosChanged, + .readGpios, + .readGpiosReply, + ] + } public init() {} } -#if swift(>=4.2) - -extension HardwareMessage.TypeEnum: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [HardwareMessage.TypeEnum] = [ - .unset, - .writeGpios, - .watchGpios, - .gpiosChanged, - .readGpios, - .readGpiosReply, - ] -} - -#endif // swift(>=4.2) - -#if swift(>=5.5) && canImport(_Concurrency) -extension HardwareMessage: @unchecked Sendable {} -extension HardwareMessage.TypeEnum: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" extension HardwareMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".HardwareMessage" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "type"), - 2: .standard(proto: "gpio_mask"), - 3: .standard(proto: "gpio_value"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}type\0\u{3}gpio_mask\0\u{3}gpio_value\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -185,12 +170,5 @@ extension HardwareMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplemen } extension HardwareMessage.TypeEnum: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "UNSET"), - 1: .same(proto: "WRITE_GPIOS"), - 2: .same(proto: "WATCH_GPIOS"), - 3: .same(proto: "GPIOS_CHANGED"), - 4: .same(proto: "READ_GPIOS"), - 5: .same(proto: "READ_GPIOS_REPLY"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0UNSET\0\u{1}WRITE_GPIOS\0\u{1}WATCH_GPIOS\0\u{1}GPIOS_CHANGED\0\u{1}READ_GPIOS\0\u{1}READ_GPIOS_REPLY\0") } diff --git a/MeshtasticProtobufs/Sources/meshtastic/rtttl.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/rtttl.pb.swift index 6fdf3208..0c114acb 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/rtttl.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/rtttl.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/rtttl.proto @@ -7,7 +8,6 @@ // 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 @@ -22,7 +22,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// /// Canned message module configuration. -public struct RTTTLConfig { +public struct RTTTLConfig: Sendable { // 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. @@ -36,19 +36,13 @@ public struct RTTTLConfig { public init() {} } -#if swift(>=5.5) && canImport(_Concurrency) -extension RTTTLConfig: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" extension RTTTLConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".RTTTLConfig" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "ringtone"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}ringtone\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { diff --git a/MeshtasticProtobufs/Sources/meshtastic/storeforward.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/storeforward.pb.swift index 54efa77b..a0534387 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/storeforward.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/storeforward.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/storeforward.proto @@ -22,7 +23,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// /// TODO: REPLACE -public struct StoreAndForward { +public struct StoreAndForward: Sendable { // 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. @@ -79,7 +80,7 @@ public struct StoreAndForward { /// /// TODO: REPLACE - public enum OneOf_Variant: Equatable { + public enum OneOf_Variant: Equatable, Sendable { /// /// TODO: REPLACE case stats(StoreAndForward.Statistics) @@ -93,38 +94,12 @@ public struct StoreAndForward { /// Text from history message. case text(Data) - #if !swift(>=4.1) - public static func ==(lhs: StoreAndForward.OneOf_Variant, rhs: StoreAndForward.OneOf_Variant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.stats, .stats): return { - guard case .stats(let l) = lhs, case .stats(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.history, .history): return { - guard case .history(let l) = lhs, case .history(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.heartbeat, .heartbeat): return { - guard case .heartbeat(let l) = lhs, case .heartbeat(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.text, .text): return { - guard case .text(let l) = lhs, case .text(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } /// /// 001 - 063 = From Router /// 064 - 127 = From Client - public enum RequestResponse: SwiftProtobuf.Enum { + public enum RequestResponse: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -242,11 +217,31 @@ public struct StoreAndForward { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [StoreAndForward.RequestResponse] = [ + .unset, + .routerError, + .routerHeartbeat, + .routerPing, + .routerPong, + .routerBusy, + .routerHistory, + .routerStats, + .routerTextDirect, + .routerTextBroadcast, + .clientError, + .clientHistory, + .clientStats, + .clientPing, + .clientPong, + .clientAbort, + ] + } /// /// TODO: REPLACE - public struct Statistics { + public struct Statistics: Sendable { // 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. @@ -294,7 +289,7 @@ public struct StoreAndForward { /// /// TODO: REPLACE - public struct History { + public struct History: Sendable { // 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. @@ -319,7 +314,7 @@ public struct StoreAndForward { /// /// TODO: REPLACE - public struct Heartbeat { + public struct Heartbeat: Sendable { // 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. @@ -340,54 +335,13 @@ public struct StoreAndForward { public init() {} } -#if swift(>=4.2) - -extension StoreAndForward.RequestResponse: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [StoreAndForward.RequestResponse] = [ - .unset, - .routerError, - .routerHeartbeat, - .routerPing, - .routerPong, - .routerBusy, - .routerHistory, - .routerStats, - .routerTextDirect, - .routerTextBroadcast, - .clientError, - .clientHistory, - .clientStats, - .clientPing, - .clientPong, - .clientAbort, - ] -} - -#endif // swift(>=4.2) - -#if swift(>=5.5) && canImport(_Concurrency) -extension StoreAndForward: @unchecked Sendable {} -extension StoreAndForward.OneOf_Variant: @unchecked Sendable {} -extension StoreAndForward.RequestResponse: @unchecked Sendable {} -extension StoreAndForward.Statistics: @unchecked Sendable {} -extension StoreAndForward.History: @unchecked Sendable {} -extension StoreAndForward.Heartbeat: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" extension StoreAndForward: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".StoreAndForward" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "rr"), - 2: .same(proto: "stats"), - 3: .same(proto: "history"), - 4: .same(proto: "heartbeat"), - 5: .same(proto: "text"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}rr\0\u{1}stats\0\u{1}history\0\u{1}heartbeat\0\u{1}text\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -487,39 +441,12 @@ extension StoreAndForward: SwiftProtobuf.Message, SwiftProtobuf._MessageImplemen } extension StoreAndForward.RequestResponse: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "UNSET"), - 1: .same(proto: "ROUTER_ERROR"), - 2: .same(proto: "ROUTER_HEARTBEAT"), - 3: .same(proto: "ROUTER_PING"), - 4: .same(proto: "ROUTER_PONG"), - 5: .same(proto: "ROUTER_BUSY"), - 6: .same(proto: "ROUTER_HISTORY"), - 7: .same(proto: "ROUTER_STATS"), - 8: .same(proto: "ROUTER_TEXT_DIRECT"), - 9: .same(proto: "ROUTER_TEXT_BROADCAST"), - 64: .same(proto: "CLIENT_ERROR"), - 65: .same(proto: "CLIENT_HISTORY"), - 66: .same(proto: "CLIENT_STATS"), - 67: .same(proto: "CLIENT_PING"), - 68: .same(proto: "CLIENT_PONG"), - 106: .same(proto: "CLIENT_ABORT"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0UNSET\0\u{1}ROUTER_ERROR\0\u{1}ROUTER_HEARTBEAT\0\u{1}ROUTER_PING\0\u{1}ROUTER_PONG\0\u{1}ROUTER_BUSY\0\u{1}ROUTER_HISTORY\0\u{1}ROUTER_STATS\0\u{1}ROUTER_TEXT_DIRECT\0\u{1}ROUTER_TEXT_BROADCAST\0\u{2}7CLIENT_ERROR\0\u{1}CLIENT_HISTORY\0\u{1}CLIENT_STATS\0\u{1}CLIENT_PING\0\u{1}CLIENT_PONG\0\u{2}&CLIENT_ABORT\0") } extension StoreAndForward.Statistics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = StoreAndForward.protoMessageName + ".Statistics" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "messages_total"), - 2: .standard(proto: "messages_saved"), - 3: .standard(proto: "messages_max"), - 4: .standard(proto: "up_time"), - 5: .same(proto: "requests"), - 6: .standard(proto: "requests_history"), - 7: .same(proto: "heartbeat"), - 8: .standard(proto: "return_max"), - 9: .standard(proto: "return_window"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}messages_total\0\u{3}messages_saved\0\u{3}messages_max\0\u{3}up_time\0\u{1}requests\0\u{3}requests_history\0\u{1}heartbeat\0\u{3}return_max\0\u{3}return_window\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -589,11 +516,7 @@ extension StoreAndForward.Statistics: SwiftProtobuf.Message, SwiftProtobuf._Mess extension StoreAndForward.History: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = StoreAndForward.protoMessageName + ".History" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "history_messages"), - 2: .same(proto: "window"), - 3: .standard(proto: "last_request"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}history_messages\0\u{1}window\0\u{3}last_request\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -633,10 +556,7 @@ extension StoreAndForward.History: SwiftProtobuf.Message, SwiftProtobuf._Message extension StoreAndForward.Heartbeat: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = StoreAndForward.protoMessageName + ".Heartbeat" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "period"), - 2: .same(proto: "secondary"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}period\0\u{1}secondary\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { diff --git a/MeshtasticProtobufs/Sources/meshtastic/telemetry.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/telemetry.pb.swift index 1173189c..5ad5fad3 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/telemetry.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/telemetry.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/telemetry.proto @@ -7,7 +8,6 @@ // 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 @@ -22,7 +22,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// /// Supported I2C Sensors for telemetry in Meshtastic -public enum TelemetrySensorType: SwiftProtobuf.Enum { +public enum TelemetrySensorType: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int /// @@ -204,6 +204,10 @@ public enum TelemetrySensorType: SwiftProtobuf.Enum { /// /// TSL2561 light sensor case tsl2561 // = 44 + + /// + /// BH1750 light sensor + case bh1750 // = 45 case UNRECOGNIZED(Int) public init() { @@ -257,6 +261,7 @@ public enum TelemetrySensorType: SwiftProtobuf.Enum { case 42: self = .sfa30 case 43: self = .sen5X case 44: self = .tsl2561 + case 45: self = .bh1750 default: self = .UNRECOGNIZED(rawValue) } } @@ -308,15 +313,11 @@ public enum TelemetrySensorType: SwiftProtobuf.Enum { case .sfa30: return 42 case .sen5X: return 43 case .tsl2561: return 44 + case .bh1750: return 45 case .UNRECOGNIZED(let i): return i } } -} - -#if swift(>=4.2) - -extension TelemetrySensorType: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. public static let allCases: [TelemetrySensorType] = [ .sensorUnset, @@ -364,14 +365,14 @@ extension TelemetrySensorType: CaseIterable { .sfa30, .sen5X, .tsl2561, + .bh1750, ] -} -#endif // swift(>=4.2) +} /// /// Key native device metrics such as battery level -public struct DeviceMetrics { +public struct DeviceMetrics: Sendable { // 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. @@ -444,7 +445,7 @@ public struct DeviceMetrics { /// /// Weather station or other environmental metrics -public struct EnvironmentMetrics { +public struct EnvironmentMetrics: @unchecked Sendable { // 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. @@ -702,7 +703,7 @@ public struct EnvironmentMetrics { /// /// Power Metrics (voltage / current / etc) -public struct PowerMetrics { +public struct PowerMetrics: Sendable { // 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. @@ -907,7 +908,7 @@ public struct PowerMetrics { /// /// Air quality metrics -public struct AirQualityMetrics { +public struct AirQualityMetrics: @unchecked Sendable { // 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. @@ -1196,7 +1197,7 @@ public struct AirQualityMetrics { /// /// Local device mesh statistics -public struct LocalStats { +public struct LocalStats: Sendable { // 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. @@ -1255,6 +1256,10 @@ public struct LocalStats { /// Number of bytes free in the heap public var heapFreeBytes: UInt32 = 0 + /// + /// Number of packets that were dropped because the transmit queue was full. + public var numTxDropped: UInt32 = 0 + public var unknownFields = SwiftProtobuf.UnknownStorage() public init() {} @@ -1262,7 +1267,7 @@ public struct LocalStats { /// /// Health telemetry metrics -public struct HealthMetrics { +public struct HealthMetrics: Sendable { // 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. @@ -1311,7 +1316,7 @@ public struct HealthMetrics { /// /// Linux host metrics -public struct HostMetrics { +public struct HostMetrics: Sendable { // 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. @@ -1385,7 +1390,7 @@ public struct HostMetrics { /// /// Types of Measurements the telemetry module is equipped to handle -public struct Telemetry { +public struct Telemetry: @unchecked Sendable { // 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. @@ -1474,7 +1479,7 @@ public struct Telemetry { public var unknownFields = SwiftProtobuf.UnknownStorage() - public enum OneOf_Variant: Equatable { + public enum OneOf_Variant: Equatable, Sendable { /// /// Key native device metrics such as battery level case deviceMetrics(DeviceMetrics) @@ -1497,44 +1502,6 @@ public struct Telemetry { /// Linux host metrics case hostMetrics(HostMetrics) - #if !swift(>=4.1) - public static func ==(lhs: Telemetry.OneOf_Variant, rhs: Telemetry.OneOf_Variant) -> Bool { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch (lhs, rhs) { - case (.deviceMetrics, .deviceMetrics): return { - guard case .deviceMetrics(let l) = lhs, case .deviceMetrics(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.environmentMetrics, .environmentMetrics): return { - guard case .environmentMetrics(let l) = lhs, case .environmentMetrics(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.airQualityMetrics, .airQualityMetrics): return { - guard case .airQualityMetrics(let l) = lhs, case .airQualityMetrics(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.powerMetrics, .powerMetrics): return { - guard case .powerMetrics(let l) = lhs, case .powerMetrics(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.localStats, .localStats): return { - guard case .localStats(let l) = lhs, case .localStats(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.healthMetrics, .healthMetrics): return { - guard case .healthMetrics(let l) = lhs, case .healthMetrics(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.hostMetrics, .hostMetrics): return { - guard case .hostMetrics(let l) = lhs, case .hostMetrics(let r) = rhs else { preconditionFailure() } - return l == r - }() - default: return false - } - } - #endif } public init() {} @@ -1544,7 +1511,7 @@ public struct Telemetry { /// /// NAU7802 Telemetry configuration, for saving to flash -public struct Nau7802Config { +public struct Nau7802Config: Sendable { // 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. @@ -1562,83 +1529,17 @@ public struct Nau7802Config { public init() {} } -#if swift(>=5.5) && canImport(_Concurrency) -extension TelemetrySensorType: @unchecked Sendable {} -extension DeviceMetrics: @unchecked Sendable {} -extension EnvironmentMetrics: @unchecked Sendable {} -extension PowerMetrics: @unchecked Sendable {} -extension AirQualityMetrics: @unchecked Sendable {} -extension LocalStats: @unchecked Sendable {} -extension HealthMetrics: @unchecked Sendable {} -extension HostMetrics: @unchecked Sendable {} -extension Telemetry: @unchecked Sendable {} -extension Telemetry.OneOf_Variant: @unchecked Sendable {} -extension Nau7802Config: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" extension TelemetrySensorType: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "SENSOR_UNSET"), - 1: .same(proto: "BME280"), - 2: .same(proto: "BME680"), - 3: .same(proto: "MCP9808"), - 4: .same(proto: "INA260"), - 5: .same(proto: "INA219"), - 6: .same(proto: "BMP280"), - 7: .same(proto: "SHTC3"), - 8: .same(proto: "LPS22"), - 9: .same(proto: "QMC6310"), - 10: .same(proto: "QMI8658"), - 11: .same(proto: "QMC5883L"), - 12: .same(proto: "SHT31"), - 13: .same(proto: "PMSA003I"), - 14: .same(proto: "INA3221"), - 15: .same(proto: "BMP085"), - 16: .same(proto: "RCWL9620"), - 17: .same(proto: "SHT4X"), - 18: .same(proto: "VEML7700"), - 19: .same(proto: "MLX90632"), - 20: .same(proto: "OPT3001"), - 21: .same(proto: "LTR390UV"), - 22: .same(proto: "TSL25911FN"), - 23: .same(proto: "AHT10"), - 24: .same(proto: "DFROBOT_LARK"), - 25: .same(proto: "NAU7802"), - 26: .same(proto: "BMP3XX"), - 27: .same(proto: "ICM20948"), - 28: .same(proto: "MAX17048"), - 29: .same(proto: "CUSTOM_SENSOR"), - 30: .same(proto: "MAX30102"), - 31: .same(proto: "MLX90614"), - 32: .same(proto: "SCD4X"), - 33: .same(proto: "RADSENS"), - 34: .same(proto: "INA226"), - 35: .same(proto: "DFROBOT_RAIN"), - 36: .same(proto: "DPS310"), - 37: .same(proto: "RAK12035"), - 38: .same(proto: "MAX17261"), - 39: .same(proto: "PCT2075"), - 40: .same(proto: "ADS1X15"), - 41: .same(proto: "ADS1X15_ALT"), - 42: .same(proto: "SFA30"), - 43: .same(proto: "SEN5X"), - 44: .same(proto: "TSL2561"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0SENSOR_UNSET\0\u{1}BME280\0\u{1}BME680\0\u{1}MCP9808\0\u{1}INA260\0\u{1}INA219\0\u{1}BMP280\0\u{1}SHTC3\0\u{1}LPS22\0\u{1}QMC6310\0\u{1}QMI8658\0\u{1}QMC5883L\0\u{1}SHT31\0\u{1}PMSA003I\0\u{1}INA3221\0\u{1}BMP085\0\u{1}RCWL9620\0\u{1}SHT4X\0\u{1}VEML7700\0\u{1}MLX90632\0\u{1}OPT3001\0\u{1}LTR390UV\0\u{1}TSL25911FN\0\u{1}AHT10\0\u{1}DFROBOT_LARK\0\u{1}NAU7802\0\u{1}BMP3XX\0\u{1}ICM20948\0\u{1}MAX17048\0\u{1}CUSTOM_SENSOR\0\u{1}MAX30102\0\u{1}MLX90614\0\u{1}SCD4X\0\u{1}RADSENS\0\u{1}INA226\0\u{1}DFROBOT_RAIN\0\u{1}DPS310\0\u{1}RAK12035\0\u{1}MAX17261\0\u{1}PCT2075\0\u{1}ADS1X15\0\u{1}ADS1X15_ALT\0\u{1}SFA30\0\u{1}SEN5X\0\u{1}TSL2561\0\u{1}BH1750\0") } extension DeviceMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".DeviceMetrics" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "battery_level"), - 2: .same(proto: "voltage"), - 3: .standard(proto: "channel_utilization"), - 4: .standard(proto: "air_util_tx"), - 5: .standard(proto: "uptime_seconds"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}battery_level\0\u{1}voltage\0\u{3}channel_utilization\0\u{3}air_util_tx\0\u{3}uptime_seconds\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -1692,30 +1593,7 @@ extension DeviceMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".EnvironmentMetrics" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "temperature"), - 2: .standard(proto: "relative_humidity"), - 3: .standard(proto: "barometric_pressure"), - 4: .standard(proto: "gas_resistance"), - 5: .same(proto: "voltage"), - 6: .same(proto: "current"), - 7: .same(proto: "iaq"), - 8: .same(proto: "distance"), - 9: .same(proto: "lux"), - 10: .standard(proto: "white_lux"), - 11: .standard(proto: "ir_lux"), - 12: .standard(proto: "uv_lux"), - 13: .standard(proto: "wind_direction"), - 14: .standard(proto: "wind_speed"), - 15: .same(proto: "weight"), - 16: .standard(proto: "wind_gust"), - 17: .standard(proto: "wind_lull"), - 18: .same(proto: "radiation"), - 19: .standard(proto: "rainfall_1h"), - 20: .standard(proto: "rainfall_24h"), - 21: .standard(proto: "soil_moisture"), - 22: .standard(proto: "soil_temperature"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}temperature\0\u{3}relative_humidity\0\u{3}barometric_pressure\0\u{3}gas_resistance\0\u{1}voltage\0\u{1}current\0\u{1}iaq\0\u{1}distance\0\u{1}lux\0\u{3}white_lux\0\u{3}ir_lux\0\u{3}uv_lux\0\u{3}wind_direction\0\u{3}wind_speed\0\u{1}weight\0\u{3}wind_gust\0\u{3}wind_lull\0\u{1}radiation\0\u{3}rainfall_1h\0\u{3}rainfall_24h\0\u{3}soil_moisture\0\u{3}soil_temperature\0") fileprivate class _StorageClass { var _temperature: Float? = nil @@ -1741,15 +1619,11 @@ extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImple var _soilMoisture: UInt32? = nil var _soilTemperature: Float? = nil - #if swift(>=5.10) // This property is used as the initial default value for new instances of the type. // The type itself is protecting the reference to its storage via CoW semantics. // This will force a copy to be made of this reference when the first mutation occurs; // hence, it is safe to mark this as `nonisolated(unsafe)`. static nonisolated(unsafe) let defaultInstance = _StorageClass() - #else - static let defaultInstance = _StorageClass() - #endif private init() {} @@ -1936,24 +1810,7 @@ extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImple extension PowerMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".PowerMetrics" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "ch1_voltage"), - 2: .standard(proto: "ch1_current"), - 3: .standard(proto: "ch2_voltage"), - 4: .standard(proto: "ch2_current"), - 5: .standard(proto: "ch3_voltage"), - 6: .standard(proto: "ch3_current"), - 7: .standard(proto: "ch4_voltage"), - 8: .standard(proto: "ch4_current"), - 9: .standard(proto: "ch5_voltage"), - 10: .standard(proto: "ch5_current"), - 11: .standard(proto: "ch6_voltage"), - 12: .standard(proto: "ch6_current"), - 13: .standard(proto: "ch7_voltage"), - 14: .standard(proto: "ch7_current"), - 15: .standard(proto: "ch8_voltage"), - 16: .standard(proto: "ch8_current"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}ch1_voltage\0\u{3}ch1_current\0\u{3}ch2_voltage\0\u{3}ch2_current\0\u{3}ch3_voltage\0\u{3}ch3_current\0\u{3}ch4_voltage\0\u{3}ch4_current\0\u{3}ch5_voltage\0\u{3}ch5_current\0\u{3}ch6_voltage\0\u{3}ch6_current\0\u{3}ch7_voltage\0\u{3}ch7_current\0\u{3}ch8_voltage\0\u{3}ch8_current\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2062,33 +1919,7 @@ extension PowerMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat extension AirQualityMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".AirQualityMetrics" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "pm10_standard"), - 2: .standard(proto: "pm25_standard"), - 3: .standard(proto: "pm100_standard"), - 4: .standard(proto: "pm10_environmental"), - 5: .standard(proto: "pm25_environmental"), - 6: .standard(proto: "pm100_environmental"), - 7: .standard(proto: "particles_03um"), - 8: .standard(proto: "particles_05um"), - 9: .standard(proto: "particles_10um"), - 10: .standard(proto: "particles_25um"), - 11: .standard(proto: "particles_50um"), - 12: .standard(proto: "particles_100um"), - 13: .same(proto: "co2"), - 14: .standard(proto: "co2_temperature"), - 15: .standard(proto: "co2_humidity"), - 16: .standard(proto: "form_formaldehyde"), - 17: .standard(proto: "form_humidity"), - 18: .standard(proto: "form_temperature"), - 19: .standard(proto: "pm40_standard"), - 20: .standard(proto: "particles_40um"), - 21: .standard(proto: "pm_temperature"), - 22: .standard(proto: "pm_humidity"), - 23: .standard(proto: "pm_voc_idx"), - 24: .standard(proto: "pm_nox_idx"), - 25: .standard(proto: "particles_tps"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}pm10_standard\0\u{3}pm25_standard\0\u{3}pm100_standard\0\u{3}pm10_environmental\0\u{3}pm25_environmental\0\u{3}pm100_environmental\0\u{3}particles_03um\0\u{3}particles_05um\0\u{3}particles_10um\0\u{3}particles_25um\0\u{3}particles_50um\0\u{3}particles_100um\0\u{1}co2\0\u{3}co2_temperature\0\u{3}co2_humidity\0\u{3}form_formaldehyde\0\u{3}form_humidity\0\u{3}form_temperature\0\u{3}pm40_standard\0\u{3}particles_40um\0\u{3}pm_temperature\0\u{3}pm_humidity\0\u{3}pm_voc_idx\0\u{3}pm_nox_idx\0\u{3}particles_tps\0") fileprivate class _StorageClass { var _pm10Standard: UInt32? = nil @@ -2117,15 +1948,11 @@ extension AirQualityMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem var _pmNoxIdx: Float? = nil var _particlesTps: Float? = nil - #if swift(>=5.10) // This property is used as the initial default value for new instances of the type. // The type itself is protecting the reference to its storage via CoW semantics. // This will force a copy to be made of this reference when the first mutation occurs; // hence, it is safe to mark this as `nonisolated(unsafe)`. static nonisolated(unsafe) let defaultInstance = _StorageClass() - #else - static let defaultInstance = _StorageClass() - #endif private init() {} @@ -2330,21 +2157,7 @@ extension AirQualityMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem extension LocalStats: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".LocalStats" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "uptime_seconds"), - 2: .standard(proto: "channel_utilization"), - 3: .standard(proto: "air_util_tx"), - 4: .standard(proto: "num_packets_tx"), - 5: .standard(proto: "num_packets_rx"), - 6: .standard(proto: "num_packets_rx_bad"), - 7: .standard(proto: "num_online_nodes"), - 8: .standard(proto: "num_total_nodes"), - 9: .standard(proto: "num_rx_dupe"), - 10: .standard(proto: "num_tx_relay"), - 11: .standard(proto: "num_tx_relay_canceled"), - 12: .standard(proto: "heap_total_bytes"), - 13: .standard(proto: "heap_free_bytes"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}uptime_seconds\0\u{3}channel_utilization\0\u{3}air_util_tx\0\u{3}num_packets_tx\0\u{3}num_packets_rx\0\u{3}num_packets_rx_bad\0\u{3}num_online_nodes\0\u{3}num_total_nodes\0\u{3}num_rx_dupe\0\u{3}num_tx_relay\0\u{3}num_tx_relay_canceled\0\u{3}heap_total_bytes\0\u{3}heap_free_bytes\0\u{3}num_tx_dropped\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2365,6 +2178,7 @@ extension LocalStats: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio case 11: try { try decoder.decodeSingularUInt32Field(value: &self.numTxRelayCanceled) }() case 12: try { try decoder.decodeSingularUInt32Field(value: &self.heapTotalBytes) }() case 13: try { try decoder.decodeSingularUInt32Field(value: &self.heapFreeBytes) }() + case 14: try { try decoder.decodeSingularUInt32Field(value: &self.numTxDropped) }() default: break } } @@ -2374,10 +2188,10 @@ extension LocalStats: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio if self.uptimeSeconds != 0 { try visitor.visitSingularUInt32Field(value: self.uptimeSeconds, fieldNumber: 1) } - if self.channelUtilization != 0 { + if self.channelUtilization.bitPattern != 0 { try visitor.visitSingularFloatField(value: self.channelUtilization, fieldNumber: 2) } - if self.airUtilTx != 0 { + if self.airUtilTx.bitPattern != 0 { try visitor.visitSingularFloatField(value: self.airUtilTx, fieldNumber: 3) } if self.numPacketsTx != 0 { @@ -2410,6 +2224,9 @@ extension LocalStats: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio if self.heapFreeBytes != 0 { try visitor.visitSingularUInt32Field(value: self.heapFreeBytes, fieldNumber: 13) } + if self.numTxDropped != 0 { + try visitor.visitSingularUInt32Field(value: self.numTxDropped, fieldNumber: 14) + } try unknownFields.traverse(visitor: &visitor) } @@ -2427,6 +2244,7 @@ extension LocalStats: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio if lhs.numTxRelayCanceled != rhs.numTxRelayCanceled {return false} if lhs.heapTotalBytes != rhs.heapTotalBytes {return false} if lhs.heapFreeBytes != rhs.heapFreeBytes {return false} + if lhs.numTxDropped != rhs.numTxDropped {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } @@ -2434,11 +2252,7 @@ extension LocalStats: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio extension HealthMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".HealthMetrics" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "heart_bpm"), - 2: .same(proto: "spO2"), - 3: .same(proto: "temperature"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}heart_bpm\0\u{1}spO2\0\u{1}temperature\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2482,17 +2296,7 @@ extension HealthMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa extension HostMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".HostMetrics" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "uptime_seconds"), - 2: .standard(proto: "freemem_bytes"), - 3: .standard(proto: "diskfree1_bytes"), - 4: .standard(proto: "diskfree2_bytes"), - 5: .standard(proto: "diskfree3_bytes"), - 6: .same(proto: "load1"), - 7: .same(proto: "load5"), - 8: .same(proto: "load15"), - 9: .standard(proto: "user_string"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}uptime_seconds\0\u{3}freemem_bytes\0\u{3}diskfree1_bytes\0\u{3}diskfree2_bytes\0\u{3}diskfree3_bytes\0\u{1}load1\0\u{1}load5\0\u{1}load15\0\u{3}user_string\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2566,30 +2370,17 @@ extension HostMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati extension Telemetry: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Telemetry" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "time"), - 2: .standard(proto: "device_metrics"), - 3: .standard(proto: "environment_metrics"), - 4: .standard(proto: "air_quality_metrics"), - 5: .standard(proto: "power_metrics"), - 6: .standard(proto: "local_stats"), - 7: .standard(proto: "health_metrics"), - 8: .standard(proto: "host_metrics"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}time\0\u{3}device_metrics\0\u{3}environment_metrics\0\u{3}air_quality_metrics\0\u{3}power_metrics\0\u{3}local_stats\0\u{3}health_metrics\0\u{3}host_metrics\0") fileprivate class _StorageClass { var _time: UInt32 = 0 var _variant: Telemetry.OneOf_Variant? - #if swift(>=5.10) // This property is used as the initial default value for new instances of the type. // The type itself is protecting the reference to its storage via CoW semantics. // This will force a copy to be made of this reference when the first mutation occurs; // hence, it is safe to mark this as `nonisolated(unsafe)`. static nonisolated(unsafe) let defaultInstance = _StorageClass() - #else - static let defaultInstance = _StorageClass() - #endif private init() {} @@ -2774,10 +2565,7 @@ extension Telemetry: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation extension Nau7802Config: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Nau7802Config" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "zeroOffset"), - 2: .same(proto: "calibrationFactor"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}zeroOffset\0\u{1}calibrationFactor\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2796,7 +2584,7 @@ extension Nau7802Config: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa if self.zeroOffset != 0 { try visitor.visitSingularInt32Field(value: self.zeroOffset, fieldNumber: 1) } - if self.calibrationFactor != 0 { + if self.calibrationFactor.bitPattern != 0 { try visitor.visitSingularFloatField(value: self.calibrationFactor, fieldNumber: 2) } try unknownFields.traverse(visitor: &visitor) diff --git a/MeshtasticProtobufs/Sources/meshtastic/xmodem.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/xmodem.pb.swift index 1f41fe0b..010c6eed 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/xmodem.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/xmodem.pb.swift @@ -1,5 +1,6 @@ // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. // Source: meshtastic/xmodem.proto @@ -20,7 +21,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP typealias Version = _2 } -public struct XModem { +public struct XModem: Sendable { // 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. @@ -35,7 +36,7 @@ public struct XModem { public var unknownFields = SwiftProtobuf.UnknownStorage() - public enum Control: SwiftProtobuf.Enum { + public enum Control: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int case nul // = 0 case soh // = 1 @@ -79,46 +80,30 @@ public struct XModem { } } + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [XModem.Control] = [ + .nul, + .soh, + .stx, + .eot, + .ack, + .nak, + .can, + .ctrlz, + ] + } public init() {} } -#if swift(>=4.2) - -extension XModem.Control: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [XModem.Control] = [ - .nul, - .soh, - .stx, - .eot, - .ack, - .nak, - .can, - .ctrlz, - ] -} - -#endif // swift(>=4.2) - -#if swift(>=5.5) && canImport(_Concurrency) -extension XModem: @unchecked Sendable {} -extension XModem.Control: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "meshtastic" extension XModem: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".XModem" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "control"), - 2: .same(proto: "seq"), - 3: .same(proto: "crc16"), - 4: .same(proto: "buffer"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}control\0\u{1}seq\0\u{1}crc16\0\u{1}buffer\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -162,14 +147,5 @@ extension XModem: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBas } extension XModem.Control: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "NUL"), - 1: .same(proto: "SOH"), - 2: .same(proto: "STX"), - 4: .same(proto: "EOT"), - 6: .same(proto: "ACK"), - 21: .same(proto: "NAK"), - 24: .same(proto: "CAN"), - 26: .same(proto: "CTRLZ"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0NUL\0\u{1}SOH\0\u{1}STX\0\u{2}\u{2}EOT\0\u{2}\u{2}ACK\0\u{2}\u{f}NAK\0\u{2}\u{3}CAN\0\u{2}\u{2}CTRLZ\0") } diff --git a/protobufs b/protobufs index c1e31a96..62ef17b3 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit c1e31a9655e9920a8b5b8eccdf7c69ef1ae42a49 +Subproject commit 62ef17b3d1625fc6d78ed661f614d0baad4be9ef From b2163877d038f9eef59fd1854fa3d685a960114c Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 4 Jan 2026 20:06:03 -0800 Subject: [PATCH 04/15] Bump version --- Meshtastic.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index b87ce5f2..cd52c496 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -2102,7 +2102,7 @@ "@executable_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 14.6; - MARKETING_VERSION = 2.7.6; + MARKETING_VERSION = 2.7.7; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -2137,7 +2137,7 @@ "@executable_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 14.6; - MARKETING_VERSION = 2.7.6; + MARKETING_VERSION = 2.7.7; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -2169,7 +2169,7 @@ "@executable_path/../../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 14.6; - MARKETING_VERSION = 2.7.6; + MARKETING_VERSION = 2.7.7; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2202,7 +2202,7 @@ "@executable_path/../../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 14.6; - MARKETING_VERSION = 2.7.6; + MARKETING_VERSION = 2.7.7; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; From cb2fd8cc15185f6b9ce8a940d8ca8d11a32a2f80 Mon Sep 17 00:00:00 2001 From: MGJ <62177301+MGJ520@users.noreply.github.com> Date: Mon, 5 Jan 2026 12:16:16 +0800 Subject: [PATCH 05/15] update the translations (#1540) update the translations --- Localizable.xcstrings | 120 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 117 insertions(+), 3 deletions(-) diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 33be032d..e4f5e806 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -2114,6 +2114,12 @@ "value" : "О Мештастику" } }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "关于" + } + }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -7604,6 +7610,12 @@ "value" : "Очисти логове" } }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "清除Log记录" + } + }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -8322,7 +8334,13 @@ "state" : "translated", "value" : "Повежи се" } - } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "连接" + } + }, } }, "Connect to a Node" : { @@ -10332,6 +10350,12 @@ "value" : "Логови сензора откривања" } }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "检测传感器记录" + } + }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -14617,6 +14641,12 @@ "value" : "Пронађи контакт" } }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "搜索联系人" + } + }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -14651,6 +14681,12 @@ "value" : "Пронађи чвор" } }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "搜索节点" + } + }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -14938,6 +14974,12 @@ "value" : "Прво откривање" } }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "首次通信" + } + }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -15930,6 +15972,12 @@ "value" : "Апсолутна подршка" } }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "官方支持" + } + }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -16880,6 +16928,12 @@ "value" : "Сакриј упозорења" } }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "通知静音" + } + }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -20388,7 +20442,13 @@ "state" : "translated", "value" : "Ручно" } - } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "更多" + } + }, } }, "Manual Configuration" : { @@ -20915,7 +20975,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Mesh 地图" + "value" : "地图" } }, "zh-Hant-TW" : { @@ -23772,6 +23832,12 @@ "value" : "Број чвора" } }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "用户编号" + } + }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -26585,6 +26651,12 @@ "value" : "Логови позиција" } }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "位置记录" + } + }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -26949,6 +27021,12 @@ "value" : "Логови метрике снаге" } }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "电源指标记录" + } + }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -29929,6 +30007,12 @@ "value" : "Улога" } }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "节点类型" + } + }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -30165,6 +30249,12 @@ "value" : "Снимач руте" } }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "路线记录" + } + }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -30341,6 +30431,12 @@ "value" : "Руте" } }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "路线" + } + }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -31557,6 +31653,12 @@ "value" : "Изабери тип разговора" } }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "请选择对话类型" + } + }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -37990,6 +38092,12 @@ "value" : "Лог праћења руте комуникације" } }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "追踪路由(Trace Route)记录" + } + }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -39802,6 +39910,12 @@ "value" : "Време рада" } }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "上线时长" + } + }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", From 79fac42097f7d7ebf7a7c24fb0a96191397a5f1e Mon Sep 17 00:00:00 2001 From: Mike Robbins Date: Sun, 4 Jan 2026 20:17:56 -0800 Subject: [PATCH 06/15] Don't alert (with sound: .default) when updating Live Activity (#1536) --- Meshtastic/Helpers/MeshPackets.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 255417c4..8b7a7423 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -881,8 +881,8 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage let meshActivity = Activity.activities.first(where: { $0.attributes.nodeNum == connectedNode }) if meshActivity != nil { Task { - await meshActivity?.update(updatedContent, alertConfiguration: alertConfiguration) - // await meshActivity?.update(updatedContent) + // await meshActivity?.update(updatedContent, alertConfiguration: alertConfiguration) + await meshActivity?.update(updatedContent) Logger.services.debug("Updated live activity.") } } From bff8ca018ba1b673ceb91ecd80b706b25c707b88 Mon Sep 17 00:00:00 2001 From: Benjamin Faershtein <119711889+RCGV1@users.noreply.github.com> Date: Sun, 4 Jan 2026 20:21:15 -0800 Subject: [PATCH 07/15] Fix adding channels (#1532) --- .../AccessoryManager+ToRadio.swift | 78 ++++++++++--------- 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/Meshtastic/Accessory/Accessory Manager/AccessoryManager+ToRadio.swift b/Meshtastic/Accessory/Accessory Manager/AccessoryManager+ToRadio.swift index 4fe2ffaf..cd1d2961 100644 --- a/Meshtastic/Accessory/Accessory Manager/AccessoryManager+ToRadio.swift +++ b/Meshtastic/Accessory/Accessory Manager/AccessoryManager+ToRadio.swift @@ -441,8 +441,6 @@ extension AccessoryManager { Logger.services.error("Error while sending saveChannelSet request. No active device.") throw AccessoryError.ioFailed("No active device") } - var i: Int32 = 0 - var myInfo: MyInfoEntity // Before we get started delete the existing channels from the myNodeInfo if !addChannels { tryClearExistingChannels() @@ -451,64 +449,74 @@ extension AccessoryManager { let decodedString = base64UrlString.base64urlToBase64() if let decodedData = Data(base64Encoded: decodedString) { let channelSet: ChannelSet = try ChannelSet(serializedBytes: decodedData) + + var myInfo: MyInfoEntity! + var i: Int32 = 0 + + if addChannels { + let fetchMyInfoRequest = MyInfoEntity.fetchRequest() + fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(deviceNum)) + + let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) + if fetchedMyInfo.count != 1 { + throw AccessoryError.appError("MyInfo not found") + } + + // We are trying to add a channel so lets get the last index + myInfo = fetchedMyInfo[0] + i = Int32(myInfo.channels?.count ?? -1) + + // Bail out if the index is negative or bigger than our max of 8 + if i < 0 || i > 8 { + throw AccessoryError.appError("Index out of range \(i)") + } + } + for cs in channelSet.settings { + if addChannels { - // We are trying to add a channel so lets get the last index - let fetchMyInfoRequest = MyInfoEntity.fetchRequest() - fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(deviceNum)) - do { - let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) - if fetchedMyInfo.count == 1 { - i = Int32(fetchedMyInfo[0].channels?.count ?? -1) - myInfo = fetchedMyInfo[0] - // Bail out if the index is negative or bigger than our max of 8 - if i < 0 || i > 8 { - throw AccessoryError.appError("Index out of range \(i)") - } - // Bail out if there are no channels or if the same channel name already exists - guard let mutableChannels = myInfo.channels!.mutableCopy() as? NSMutableOrderedSet else { - throw AccessoryError.appError("No channels or channel") - } - if mutableChannels.first(where: {($0 as AnyObject).name == cs.name }) is ChannelEntity { - throw AccessoryError.appError("Channel already exists") - } - } - } catch { - Logger.data.error("Failed to find a node MyInfo to save these channels to: \(error.localizedDescription, privacy: .public)") + guard let mutableChannels = myInfo.channels?.mutableCopy() as? NSMutableOrderedSet else { + throw AccessoryError.appError("No channels or channel") + } + + // Bail out if there are no channels or if the same channel name already exists + if mutableChannels.first(where: { ($0 as AnyObject).name == cs.name }) is ChannelEntity { + throw AccessoryError.appError("Channel already exists") } } var chan = Channel() - if i == 0 { - chan.role = Channel.Role.primary - } else { - chan.role = Channel.Role.secondary - } + chan.role = (i == 0) ? .primary : .secondary chan.settings = cs chan.index = i i += 1 var adminPacket = AdminMessage() adminPacket.setChannel = chan - var meshPacket: MeshPacket = MeshPacket() + + var meshPacket = MeshPacket() meshPacket.to = UInt32(deviceNum) - meshPacket.from = UInt32(deviceNum) + meshPacket.from = UInt32(deviceNum) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Date: Sun, 4 Jan 2026 23:22:58 -0500 Subject: [PATCH 08/15] Full translation into Spanish (#1529) --- Localizable.xcstrings | 7024 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 7013 insertions(+), 11 deletions(-) diff --git a/Localizable.xcstrings b/Localizable.xcstrings index e4f5e806..78c9454b 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -6,6 +6,12 @@ }, "\t%@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "\t%@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -41,6 +47,12 @@ }, " %@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : " %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -134,6 +146,12 @@ }, ": %d" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : ": %d" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -169,6 +187,12 @@ }, "(Re)define PIN_GPS_EN for your board." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "(Re)defina PIN_GPS_EN para su placa." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -203,6 +227,12 @@ }, "%@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -326,6 +356,12 @@ "value" : "%1$@ - %2$@ Towards %3$@ Back" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ - %@ Hacia %@ Atrás" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -366,6 +402,12 @@ "value" : "%@ - Keine Antwort" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ - Sin respuesta" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -406,6 +448,12 @@ "value" : "%@ - Nicht gesendet" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ - No enviado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -528,6 +576,12 @@ "value" : "%1$@ %2$lld" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ %lld" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -569,6 +623,12 @@ "value" : "%@ entfernt" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ lejos" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -609,6 +669,12 @@ "value" : "%@ kann bis zu %@ Byte lang sein." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ puede tener hasta %@ bytes de longitud." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -643,6 +709,12 @@ }, "%@ Channels?" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ ¿Canales?" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -677,6 +749,12 @@ }, "%@ config data was requested via PKC admin but no response has been returned from the remote node." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se solicitaron %@ datos de configuración a través del administrador de PKC pero no se obtuvo respuesta del nodo remoto." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -687,6 +765,12 @@ }, "%@ dB" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@dB" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -768,6 +852,12 @@ "value" : "%1$@: %2$lld / %3$lld" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@: %lld / %lld" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -803,6 +893,12 @@ }, "%@%%" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@%%" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -837,6 +933,12 @@ }, "%@°F" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@°F" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -871,6 +973,12 @@ }, "%@mA" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@mA" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -899,6 +1007,12 @@ }, "%@V" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@V" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -927,6 +1041,12 @@ }, "%d" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -979,6 +1099,12 @@ } } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d saltos" + } + }, "it" : { "variations" : { "plural" : { @@ -1061,6 +1187,12 @@ }, "%d%%" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d%%" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -1095,6 +1227,12 @@ }, "%f%%" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%f%%" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -1105,6 +1243,12 @@ }, "%lf" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lf" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -1139,6 +1283,12 @@ }, "%lld" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -1179,6 +1329,12 @@ "value" : "%1$lld %2$@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld %@" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -1225,6 +1381,12 @@ } } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld características" + } + }, "sr" : { "variations" : { "plural" : { @@ -1259,6 +1421,12 @@ "value" : "%lld oder weniger Hops entfernt" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld o menos salta de distancia" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -1287,6 +1455,12 @@ }, "%lld Readings Total" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld lecturas totales" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -1315,6 +1489,12 @@ }, "%lld Total Detection Events" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld total de eventos de detección" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -1343,6 +1523,12 @@ }, "%lld%%" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld%%" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -1383,6 +1569,12 @@ "value" : "%llddb Übertragungsleistung" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%llddb Potencia de transmisión" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -1423,6 +1615,12 @@ "value" : "%llddBm Übertragungsleistung" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%llddBm Potencia de transmisión" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -1454,6 +1652,12 @@ }, "< 1%" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "< 1%" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -1486,12 +1690,32 @@ } } }, +<<<<<<< Updated upstream "⚠️ The configured value: (%@) is not one of the optimized options." : { "comment" : "A warning label below the picker, indicating that the selected update interval is not one of the optimized options.", "isCommentAutoGenerated" : true +======= + "⚠️ The configured value: (%@ seconds) is not one of the optimized options." : { + "comment" : "A text warning that the configured update interval is not one of the optimized options.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ El valor configurado: (%@ segundos) no es una de las opciones optimizadas." + } + } + } +>>>>>>> Stashed changes }, "🦕 End of life Version 🦖 ☄️" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "🦕 Versión de fin de vida 🦖 ☄️" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -1520,6 +1744,12 @@ }, "0" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "0" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -1531,6 +1761,12 @@ }, "1" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "1" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -1542,6 +1778,12 @@ }, "1 byte" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 byte" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -1576,6 +1818,12 @@ }, "1 hop away" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 salto de distancia" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -1610,6 +1858,12 @@ }, "2.4 Ghz" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "2,4 GHz" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -1638,6 +1892,12 @@ }, "7" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "7" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -1672,6 +1932,12 @@ }, "12 Hour Clock" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reloj de 12 horas" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -1688,6 +1954,12 @@ }, "25" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "25" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -1722,6 +1994,12 @@ }, "50" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "50" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -1756,6 +2034,12 @@ }, "75" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "75" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -1790,6 +2074,12 @@ }, "100" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "100" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -1824,6 +2114,12 @@ }, "128 bit" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "128 bits" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -1858,6 +2154,12 @@ }, "180" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "180" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -1869,6 +2171,12 @@ }, "256 bit" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "256 bits" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -1903,6 +2211,12 @@ }, "A channel index of 0 indicates the primary channel where broadcast packets are sent from. Location data is broadcast from the first channel where it is enabled with firmware 2.7 forward." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Un índice de canal de 0 indica el canal principal desde donde se envían los paquetes de transmisión. Los datos de ubicación se transmiten desde el primer canal donde está habilitado con el firmware 2.7 en adelante." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -1913,6 +2227,12 @@ }, "A green lock means the channel is securely encrypted with either a 128 or 256 bit AES key." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Un candado verde significa que el canal está cifrado de forma segura con una clave AES de 128 o 256 bits." + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -1935,6 +2255,12 @@ "value" : "In a Meshtastic LoRa Mesh there are up to 8 channels. The first one is the Primary channel where most activity happens and is required. If you don't share your primary channel your first shared channel becomes the primary channel on the other network. It talks on its primary and your secondary channel. A channel with the name 'admin' controls nodes remotely. Other channels are for private groups, each with its own key." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Un código QR Meshtastic contiene la configuración de LoRa y los valores de canal necesarios para que las radios se comuniquen. Puede compartir una configuración de canal completa usando la opción Reemplazar canales; si elige Agregar canales, sus canales compartidos se agregarán a los canales de la radio receptora." + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -1993,6 +2319,12 @@ }, "A red open lock means the channel is not securely encrypted and is used for precise location data, it uses either no key at all or a 1 byte known key." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Un candado rojo abierto significa que el canal no está cifrado de forma segura y se utiliza para datos de ubicación precisos; no utiliza ninguna clave o utiliza una clave conocida de 1 byte." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -2003,6 +2335,12 @@ }, "A red open lock with a warning means the channel is not securely encrypted and is used for precise location data which is being uplinked to the internet via MQTT, it uses either no key at all or a 1 byte known key." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Un candado rojo abierto con una advertencia significa que el canal no está cifrado de forma segura y se utiliza para datos de ubicación precisos que se conectan a Internet a través de MQTT; no utiliza ninguna clave o utiliza una clave conocida de 1 byte." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -2013,6 +2351,12 @@ }, "A Trace Route was sent, no response has been received." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se envió una ruta de rastreo y no se recibió respuesta." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -2042,6 +2386,12 @@ "A yellow open lock lock means the channel is not securely encrypted but it not used for precise location data, it uses either no key at all or a 1 byte known key." : { "extractionState" : "stale", "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Un candado abierto amarillo significa que el canal no está cifrado de forma segura pero no se utiliza para datos de ubicación precisos; no utiliza ninguna clave o utiliza una clave conocida de 1 byte." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -2052,7 +2402,15 @@ }, "A yellow open lock means the channel is not securely encrypted but it is not used for precise location data, it uses either no key at all or a 1 byte known key." : { "comment" : "A description of a yellow open lock in the Channels Help view.", - "isCommentAutoGenerated" : true + "isCommentAutoGenerated" : true, + "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Un candado abierto amarillo significa que el canal no está cifrado de forma segura, pero no se utiliza para datos de ubicación precisos; no utiliza ninguna clave o utiliza una clave conocida de 1 byte." + } + } + } }, "About" : { "localizations" : { @@ -2062,6 +2420,12 @@ "value" : "Über Meshtastic" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Acerca de" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -2096,6 +2460,12 @@ "value" : "Über Meshtastic" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Acerca de Meshtastic" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -2136,6 +2506,12 @@ "value" : "Genauigkeit %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exactitud %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -2164,6 +2540,12 @@ }, "Ack SNR: %@ dB" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Confirmación SNR: %@ dB" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -2192,6 +2574,12 @@ }, "Ack Time: %@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tiempo de confirmación: %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -2226,6 +2614,12 @@ "value" : "Bestätigt" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Admitido" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -2284,6 +2678,12 @@ }, "Acknowledged by another node" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reconocido por otro nodo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -2318,6 +2718,12 @@ "value" : "Aktionen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comportamiento" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -2352,6 +2758,12 @@ "value" : "Aktiv" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -2386,6 +2798,12 @@ "value" : "Aktivität" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actividad" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -2426,6 +2844,12 @@ "value" : "ADC Override" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anulación del ADC" + } + }, "he" : { "stringUnit" : { "state" : "translated", @@ -2472,6 +2896,12 @@ }, "Add Channel" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Agregar canal" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -2500,6 +2930,12 @@ }, "Add Channels" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Agregar canales" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -2528,6 +2964,12 @@ }, "Add Contact" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Agregar contacto" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -2550,6 +2992,12 @@ }, "Add Meshtastic Node %@ as a contact" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Agregar Meshtastic Node %@ como contacto" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -2584,6 +3032,12 @@ "value" : "Zu Favoriten hinzufügen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Añadir a favoritos" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -2612,6 +3066,12 @@ }, "Additional help" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ayuda adicional" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -2646,6 +3106,12 @@ }, "Address" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dirección" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -2680,6 +3146,12 @@ }, "Admin Keys" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Claves de administrador" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -2696,6 +3168,12 @@ }, "Administration" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Administración" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -2730,6 +3208,12 @@ }, "Administration Enabled" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Administración habilitada" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -2752,6 +3236,12 @@ }, "Advanced" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avanzado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -2786,6 +3276,12 @@ }, "Advanced Device GPS" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dispositivo GPS avanzado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -2821,6 +3317,12 @@ "Advanced GPIO Options" : { "extractionState" : "stale", "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opciones GPIO avanzadas" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -2855,6 +3357,12 @@ }, "Advanced Position Flags" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Banderas de posición avanzadas" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -2895,6 +3403,12 @@ "value" : "Nach" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Después" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -2965,6 +3479,12 @@ } } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Después de %lld días" + } + }, "ja" : { "variations" : { "plural" : { @@ -3011,6 +3531,12 @@ "value" : "Nach dem Ändern der Einstellungen wird das Gerät neu starten." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Después de guardar los valores de configuración, el nodo se reiniciará." + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -3075,6 +3601,12 @@ "value" : "Nachmittag" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tarde" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -3109,6 +3641,12 @@ "value" : "Airtime" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tiempo en antena" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -3167,6 +3705,12 @@ }, "Alert" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alerta" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -3195,6 +3739,12 @@ }, "Alert GPIO buzzer when receiving a bell" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alerta al zumbador GPIO al recibir una campana" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -3229,6 +3779,12 @@ }, "Alert GPIO buzzer when receiving a message" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alerta al zumbador GPIO al recibir un mensaje" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -3257,6 +3813,12 @@ }, "Alert GPIO vibra motor when receiving a bell" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alertar al motor de vibración GPIO al recibir una campana" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -3291,6 +3853,12 @@ }, "Alert GPIO vibra motor when receiving a message" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alertar al motor de vibración GPIO al recibir un mensaje" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -3319,6 +3887,12 @@ }, "Alert when receiving a bell" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alerta al recibir una campana" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -3353,6 +3927,12 @@ }, "Alert when receiving a message" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alerta al recibir un mensaje" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -3387,6 +3967,12 @@ "value" : "Alle" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Todo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -3421,6 +4007,12 @@ }, "Allow Position Requests" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Permitir solicitudes de posición" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -3449,6 +4041,12 @@ }, "Alt" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alt." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -3483,6 +4081,12 @@ "value" : "Höhe" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Altitud" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -3517,6 +4121,12 @@ "value" : "Höhe %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Altitud %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -3545,6 +4155,12 @@ }, "Altitude Geoidal Separation" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Separación geoideal de altitud" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -3573,6 +4189,12 @@ }, "Altitude is Mean Sea Level" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La altitud es el nivel medio del mar." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -3607,6 +4229,12 @@ "value" : "Immer an" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Siempre encendido" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -3671,6 +4299,12 @@ "value" : "Immer nach Norden zeigen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apunta siempre al norte" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -3705,6 +4339,12 @@ "value" : "Ambientebeleuchtung" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Iluminación ambiental" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -3763,6 +4403,12 @@ "value" : "Ambientebeleuchtungskonfiguration" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de iluminación ambiental" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -3815,6 +4461,12 @@ }, "Ambient Lighting module config received: %@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración del módulo de iluminación ambiental recibida: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -3879,6 +4531,12 @@ "value" : "Ein quelloffenes, netzunabhängiges, dezentrales Mesh-Netzwerk, das auf kostengünstigen, stromsparenden Funkgeräten läuft." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Una red de malla descentralizada, fuera de la red y de código abierto que funciona con radios asequibles y de bajo consumo." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -3911,8 +4569,29 @@ } } }, +<<<<<<< Updated upstream +======= + "Anonymous Usage and Crash data" : { + "comment" : "A description of how the app collects and uses data about its usage and crashes. It emphasizes that this data is anonymous and non-personally identifiable.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datos anónimos de uso y fallos" + } + } + } + }, +>>>>>>> Stashed changes "Any missed messages will be delivered again." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cualquier mensaje perdido se entregará nuevamente." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -3953,6 +4632,12 @@ "value" : "Client (Standard) - Mit App verbundener Client." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aplicación conectada o dispositivo de mensajería independiente." + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -4017,6 +4702,12 @@ "value" : "App-Daten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datos de la aplicación" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -4051,6 +4742,12 @@ }, "App Files" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Archivos de aplicación" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -4085,6 +4782,12 @@ }, "App Icon" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Icono de aplicación" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -4101,6 +4804,12 @@ "value" : "Mitteilungseinstellungen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notificaciones de aplicaciones" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -4117,6 +4826,12 @@ "value" : "App-Einstellungen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de la aplicación" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -4151,6 +4866,12 @@ }, "Apple Apps" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aplicaciones de Apple" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -4191,6 +4912,12 @@ "value" : "Ungefährer Standort" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ubicación aproximada" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -4219,6 +4946,12 @@ }, "Are you sure you want to delete this message?" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Estás seguro de que deseas eliminar este mensaje?" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -4259,6 +4992,12 @@ "value" : "Bist du sicher dass du den Knoten auf die Werkseinstellungen zurücksetzen willst?" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Está seguro de que desea restablecer el nodo a los valores de fábrica?" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -4299,6 +5038,12 @@ "value" : "Bist Du sicher?" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Está seguro?" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -4357,6 +5102,12 @@ }, "Australia / New Zealand" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Australia / Nueva Zelanda" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -4385,6 +5136,12 @@ }, "Automatically Connect" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectar automáticamente" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -4395,6 +5152,12 @@ }, "Automatically toggles to the next page on the screen like a carousel, based the specified interval." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cambia automáticamente a la página siguiente en la pantalla como un carrusel, según el intervalo especificado." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -4435,6 +5198,12 @@ "value" : "Verfügbare Modem-Voreinstellungen, Standard ist „Long Range - Fast“." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Presets de módem disponibles, el valor predeterminado es Long Fast." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -4475,6 +5244,12 @@ "value" : "Geräte in der Nähe" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Radios disponibles" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -4539,6 +5314,12 @@ "value" : "Zurück" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Atrás" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -4597,6 +5378,12 @@ }, "Backup" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Respaldo" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -4613,6 +5400,12 @@ }, "Backup your private key to your iCloud keychain." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Haga una copia de seguridad de su clave privada en su llavero de iCloud." + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -4629,6 +5422,12 @@ }, "Bad" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Malo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -4657,6 +5456,12 @@ }, "Bad Request" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Solicitud incorrecta" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -4721,6 +5526,12 @@ "value" : "Bandbreite" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ancho de banda" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -4755,6 +5566,12 @@ }, "Bar" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bar" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -4789,6 +5606,12 @@ }, "Bar Series" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Serie de barras" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -4823,6 +5646,12 @@ }, "Barometric Pressure" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Presión barométrica" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -4863,6 +5692,12 @@ "value" : "Batterie" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Batería" + } + }, "he" : { "stringUnit" : { "state" : "translated", @@ -4922,6 +5757,12 @@ "value" : "Batterie Ladung" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nivel de batería" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -4987,6 +5828,12 @@ "value" : "Batterie Ladung %" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "% del nivel de batería" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -5052,6 +5899,12 @@ "value" : "Batterie Ladung %d" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nivel de batería %d" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -5110,6 +5963,12 @@ }, "Baud" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "baudios" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -5156,6 +6015,12 @@ "value" : "Biken" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ciclismo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -5190,6 +6055,12 @@ }, "BLE" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "BLE" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -5230,6 +6101,12 @@ "value" : "Die Bluetooth Pin muss 6 Stellen lang sein." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El pin BLE debe tener 6 dígitos." + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -5294,6 +6171,12 @@ "value" : "Bluetooth" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "bluetooth" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -5358,6 +6241,12 @@ "value" : "Bluetooth Konfiguration" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de Bluetooth" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -5422,6 +6311,12 @@ "value" : "Bluetooth Konfiguration empfangen: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de Bluetooth recibida: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -5482,6 +6377,12 @@ "comment" : "A heading displayed on a view that guides users to configure Bluetooth connectivity for the app.", "isCommentAutoGenerated" : true, "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectividad Bluetooth" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -5492,6 +6393,12 @@ }, "Bold Heading" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Encabezado en negrita" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -5502,6 +6409,12 @@ }, "Bold the heading text on the screen." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Negrita el texto del encabezado en la pantalla." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -5511,10 +6424,23 @@ } }, "Broadcast Device Metrics" : { - + "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Métricas de dispositivos de transmisión" + } + } + } }, "Broadcast Interval" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intervalo de transmisión" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -5555,6 +6481,12 @@ "value" : "Sendet GPS-Positionspakete mit Priorität." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Transmite paquetes de posición GPS como prioridad." + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -5619,6 +6551,12 @@ "value" : "Sendet den Standort regelmäßig als Nachricht an den Standardkanal, um die Suche nach dem Gerät zu unterstützen." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Transmite la ubicación como mensaje al canal predeterminado con regularidad para ayudar con la recuperación del dispositivo." + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -5683,6 +6621,12 @@ "value" : "Sendet Telemetriepakete mit Priorität." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Transmite paquetes de telemetría como prioridad." + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -5741,6 +6685,12 @@ }, "Button GPIO" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Botón GPIO" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -5775,6 +6725,12 @@ }, "Buy Complete Radios" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comprar Radios Completas" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -5809,6 +6765,12 @@ }, "Buzzer GPIO" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zumbador GPIO" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -5843,6 +6805,12 @@ }, "By enabling this feature, you acknowledge and expressly consent to the transmission of your device’s real-time geographic location over the MQTT protocol without encryption. This location data may be used for purposes such as live map reporting, device tracking, and related telemetry functions." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Al habilitar esta función, usted reconoce y acepta expresamente la transmisión de la ubicación geográfica en tiempo real de su dispositivo a través del protocolo MQTT sin cifrado. Estos datos de ubicación se pueden utilizar para fines tales como informes de mapas en vivo, seguimiento de dispositivos y funciones de telemetría relacionadas." + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -5871,6 +6839,12 @@ "value" : "Bytes" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "bytes" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -5930,6 +6904,12 @@ "Bytes Used" : { "comment" : "VoiceOver value for bytes used", "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bytes utilizados" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -5952,6 +6932,12 @@ "value" : "Rufzeichen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Señal de llamada" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -5992,6 +6978,12 @@ "value" : "Das Rufzeichen darf nicht leer sein." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El indicativo de llamada no debe estar vacío" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -6032,6 +7024,12 @@ "value" : "Abbrechen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancelar" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -6090,6 +7088,12 @@ }, "Canned Message module config received: %@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración del módulo de mensajes predefinidos recibida: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -6154,6 +7158,12 @@ "value" : "Canned Messages" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mensajes enlatados" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -6218,6 +7228,12 @@ "value" : "Canned Messages Config" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de mensajes predefinidos" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -6276,6 +7292,12 @@ }, "Canned Messages Messages Received For: %@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mensajes almacenados Mensajes recibidos para: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -6334,6 +7356,12 @@ }, "Carousel Interval" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intervalo de carrusel" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -6368,6 +7396,12 @@ "value" : "Kategorien" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Categorías" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -6408,6 +7442,12 @@ "value" : "Kategorie" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Categoría" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -6436,6 +7476,12 @@ }, "Ch1 Current" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Corriente Ch1" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -6464,6 +7510,12 @@ }, "Ch1 Voltage" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voltaje Ch1" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -6492,6 +7544,12 @@ }, "Ch2 Current" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Corriente Ch2" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -6520,6 +7578,12 @@ }, "Ch2 Voltage" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voltaje Ch2" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -6548,6 +7612,12 @@ }, "Ch3 Current" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ch3 actual" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -6576,6 +7646,12 @@ }, "Ch3 Voltage" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voltaje Ch3" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -6610,6 +7686,12 @@ "value" : "Kanal" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Canal" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -6668,6 +7750,12 @@ }, "Channel 0 Included" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Canal 0 incluido" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -6702,6 +7790,12 @@ }, "Channel 1" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Canal 1" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -6730,6 +7824,12 @@ }, "Channel 1 Included" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Canal 1 incluido" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -6764,6 +7864,12 @@ }, "Channel 2" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Canal 2" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -6792,6 +7898,12 @@ }, "Channel 2 Included" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Canal 2 incluido" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -6826,6 +7938,12 @@ }, "Channel 3" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Canal 3" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -6854,6 +7972,12 @@ }, "Channel 3 Included" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Canal 3 incluido" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -6888,6 +8012,12 @@ }, "Channel 4 Included" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Canal 4 incluido" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -6922,6 +8052,12 @@ }, "Channel 5 Included" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Canal 5 incluido" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -6956,6 +8092,12 @@ }, "Channel 6 Included" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Canal 6 incluido" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -6990,6 +8132,12 @@ }, "Channel 7 Included" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Canal 7 incluido" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -7024,6 +8172,12 @@ }, "Channel Details" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Detalles del canal" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -7058,6 +8212,12 @@ }, "Channel Name" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre del canal" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -7092,6 +8252,12 @@ }, "Channel number must be between 0 and 7." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El número de canal debe estar entre 0 y 7." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -7126,6 +8292,12 @@ }, "Channel Role" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rol del canal" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -7160,6 +8332,12 @@ }, "Channel URL" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL del canal" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -7194,6 +8372,12 @@ "value" : "Kanalbelegung" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utilización del canal" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -7252,6 +8436,12 @@ }, "Channel Utilization %@%%" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utilización del canal %@%%" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -7286,6 +8476,12 @@ "value" : "Kanäle" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Canales" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -7344,6 +8540,12 @@ }, "Channels being added from the QR code did not save. When adding channels the names must be unique." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los canales que se agregaron desde el código QR no se guardaron. Al agregar canales, los nombres deben ser únicos." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -7378,6 +8580,12 @@ }, "Channels Help" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ayuda de canales" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -7394,6 +8602,12 @@ }, "Chart" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cuadro" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -7428,6 +8642,12 @@ }, "CHG" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "CHG" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -7456,6 +8676,12 @@ }, "China" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Porcelana" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -7484,6 +8710,12 @@ }, "Chirpy" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alegre" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -7494,6 +8726,12 @@ }, "Clear" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Claro" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -7534,6 +8772,12 @@ "value" : "App-Daten löschen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Borrar datos de la aplicación" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -7592,6 +8836,12 @@ }, "Clear Log" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Borrar registro" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -7632,6 +8882,12 @@ "value" : "Veraltete Knoten löschen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Borrar nodos obsoletos" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -7648,6 +8904,12 @@ }, "Client" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cliente" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -7692,6 +8954,12 @@ "value" : "Client - Versteckt" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cliente oculto" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -7726,6 +8994,12 @@ }, "Client History" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Historial del cliente" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -7760,6 +9034,12 @@ }, "Client History Request Sent" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Solicitud de historial del cliente enviada" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -7794,6 +9074,12 @@ }, "Client Mute" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Silencio del cliente" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -7828,6 +9114,12 @@ }, "Client options" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opciones del cliente" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -7862,6 +9154,12 @@ }, "Clockwise Rotary Event" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Evento giratorio en el sentido de las agujas del reloj" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -7902,6 +9200,12 @@ "value" : "Schließen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cerca" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -7960,6 +9264,12 @@ }, "Coding Rate" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tasa de codificación" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -8000,6 +9310,12 @@ "value" : "Farbe" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Color" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -8040,6 +9356,12 @@ "value" : "Bleibe mit deinen Freunden und deiner Community in Verbindung, auch abseits vom Mobilfunknetz." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comuníquese fuera de la red con sus amigos y su comunidad sin servicio celular." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -8050,6 +9372,12 @@ }, "Communicating" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comunicado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -8078,6 +9406,12 @@ }, "Community Support" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apoyo comunitario" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -8109,6 +9443,12 @@ }, "Config" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "configuración" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -8149,6 +9489,12 @@ "value" : "Konfiguration für: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración para: %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -8177,6 +9523,12 @@ }, "Configuration Presets" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Preajustes de configuración" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -8217,6 +9569,12 @@ "value" : "Konfigurieren" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurar" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -8247,6 +9605,12 @@ "comment" : "Button label to guide users to configure Bluetooth connectivity for the app.", "isCommentAutoGenerated" : true, "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurar la conectividad Bluetooth" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -8259,6 +9623,12 @@ "comment" : "Button label to configure local network access permissions.", "isCommentAutoGenerated" : true, "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurar el acceso a la red local" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -8275,6 +9645,12 @@ "value" : "Standortberechtigungen konfigurieren" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurar permisos de ubicación" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -8291,6 +9667,12 @@ "value" : "Mitteilungsberechtigungen konfigurieren" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurar permisos de notificación" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -8301,6 +9683,12 @@ }, "Confirm" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Confirmar" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -8329,6 +9717,12 @@ }, "Connect" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectar" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -8351,6 +9745,12 @@ "value" : "Verbunden mit einem Knoten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectarse a un nodo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -8379,6 +9779,12 @@ }, "Connect to MQTT via Proxy" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conéctese a MQTT a través de Proxy" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -8401,6 +9807,12 @@ }, "Connect to new radio?" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Conectar a una nueva radio?" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -8435,6 +9847,12 @@ "value" : "Derzeit verbunden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectado" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -8499,6 +9917,12 @@ "value" : "Verbunden mit Knoten %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nodo conectado %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -8527,6 +9951,12 @@ }, "Connected Radio" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Radio conectada" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -8561,6 +9991,12 @@ "value" : "Verbinde..." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectando. ." + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -8619,6 +10055,12 @@ }, "Connecting to a new radio will clear all app data on the phone." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Al conectarse a una nueva radio se borrarán todos los datos de la aplicación en el teléfono." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -8653,6 +10095,12 @@ "value" : "Verbindungsversuch %lld von 10" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intento de conexión %lld de 10" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -8687,6 +10135,12 @@ }, "Connection Name" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre de conexión" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -8697,6 +10151,12 @@ }, "Consent to Share Unencrypted Node Data via MQTT" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Consentimiento para compartir datos de nodos no cifrados a través de MQTT" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -8725,6 +10185,12 @@ "value" : "Kontaktfilter" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filtros de contacto" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -8735,6 +10201,12 @@ }, "Contact URL" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL de contacto" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -8763,6 +10235,12 @@ "value" : "Kontakte (%@)" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contactos (%@)" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -8821,6 +10299,12 @@ }, "Control Type" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tipo de control" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -8855,6 +10339,12 @@ }, "Controls the blinking LED on the device. For most devices this will control one of the up to 4 LEDS, the charger and GPS LEDs are not controllable." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Controla el LED parpadeante en el dispositivo. Para la mayoría de los dispositivos, esto controlará uno de los hasta 4 LED, los LED del cargador y del GPS no son controlables." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -8895,6 +10385,12 @@ "value" : "Konvexe Hülle" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Casco convexo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -8929,6 +10425,12 @@ "value" : "Koordinate" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Coordinar" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -8963,6 +10465,12 @@ "value" : "Koordinate %1$@, %2$@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Coordinar %@, %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -9003,6 +10511,12 @@ "value" : "Koordinaten:" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Coordenadas:" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -9037,6 +10551,12 @@ "value" : "Kopieren" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copiar" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -9101,6 +10621,12 @@ "value" : "Knoten nicht gefunden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se pudo encontrar el nodo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -9135,6 +10661,12 @@ }, "Counter Clockwise Rotary Event" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Evento giratorio en sentido antihorario" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -9169,6 +10701,12 @@ "value" : "Wegpunkt erstellen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crear punto de referencia" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -9203,6 +10741,12 @@ "value" : "Erstelle deine eigenen Netzwerke" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crea tus propias redes" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -9219,6 +10763,12 @@ "value" : "Erstellt: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Creado: %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -9253,6 +10803,12 @@ "value" : "Kritische Hinweise" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alertas críticas" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -9263,6 +10819,12 @@ }, "Current" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actual" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -9297,6 +10859,12 @@ "value" : "Aktuelle Firmware Version: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versión de firmware actual: %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -9337,6 +10905,12 @@ "value" : "Aktuelle Firmware Version: %1$@, neuste Firmware Version %2$@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versión de firmware actual: %@, Última versión de firmware: %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -9377,6 +10951,12 @@ "value" : "Aktuell: %lld" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actual: %lld" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -9405,6 +10985,12 @@ }, "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" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actualmente, la forma recomendada de actualizar dispositivos ESP32 es utilizar el flash web en una computadora de escritorio desde un navegador basado en Chrome. No funciona en dispositivos móviles ni a través de BLE." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -9445,6 +11031,12 @@ "value" : "Datum" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fecha" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -9473,6 +11065,12 @@ }, "Debug" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Depurar" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -9513,6 +11111,12 @@ "value" : "Fehlersuchprotokolle" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Registros de depuración" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -9541,6 +11145,12 @@ }, "Debug Logs%@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Registros de depuración%@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -9575,6 +11185,12 @@ "value" : "Standard" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Por defecto" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -9633,6 +11249,12 @@ }, "Default 128x64 screen layout" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Diseño de pantalla predeterminado de 128x64" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -9673,6 +11295,12 @@ "value" : "Löschen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Borrar" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -9731,6 +11359,12 @@ }, "Delete all config, keys and BLE bonds? " : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Eliminar todas las configuraciones, claves y enlaces BLE?" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -9753,6 +11387,12 @@ }, "Delete all config? " : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Eliminar todas las configuraciones?" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -9775,6 +11415,12 @@ }, "Delete all device metrics?" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Eliminar todas las métricas del dispositivo?" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -9833,6 +11479,12 @@ }, "Delete all environment metrics?" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Eliminar todas las métricas del entorno?" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -9867,6 +11519,12 @@ }, "Delete all pax data?" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Eliminar todos los datos de los pasajeros?" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -9901,6 +11559,12 @@ }, "Delete all positions?" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Eliminar todas las posiciones?" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -9929,6 +11593,12 @@ }, "Delete Message" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eliminar mensaje" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -9957,6 +11627,12 @@ }, "Delete Messages" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eliminar mensajes" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -9991,6 +11667,12 @@ "value" : "Knoten löschen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eliminar nodo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -10025,6 +11707,12 @@ "value" : "Knoten löschen?" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Eliminar nodo?" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -10053,6 +11741,12 @@ }, "Delete Power metrics?" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Eliminar métricas de potencia?" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -10087,6 +11781,12 @@ "value" : "Beschreibung" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Descripción" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -10115,6 +11815,12 @@ }, "Description must be less than 100 bytes" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La descripción debe tener menos de 100 bytes." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -10149,6 +11855,12 @@ }, "Details..." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Detalles..." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -10159,6 +11871,12 @@ }, "Detection" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Detección" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -10187,6 +11905,12 @@ }, "Detection event" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Evento de detección" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -10222,6 +11946,12 @@ "value" : "Detection Sensor" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensor de detección" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -10280,6 +12010,12 @@ }, "Detection Sensor Config" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración del sensor de detección" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -10332,6 +12068,12 @@ }, "Detection Sensor Log" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Registro del sensor de detección" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -10366,6 +12108,12 @@ }, "Detection sensor messages are received as text messages. If you enable notifications you will recieve a notification for each detection message received and a corresponding unread message badge." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los mensajes del sensor de detección se reciben como mensajes de texto. Si habilita las notificaciones, recibirá una notificación por cada mensaje de detección recibido y la correspondiente insignia de mensaje no leído." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -10400,6 +12148,12 @@ }, "Detection Sensor module config received: %@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración del módulo del sensor de detección recibida: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -10458,6 +12212,12 @@ }, "Developers" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desarrolladores" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -10498,6 +12258,12 @@ "value" : "Gerät" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dispositivo" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -10562,6 +12328,12 @@ "value" : "Gerätekonfiguration" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración del dispositivo" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -10626,6 +12398,12 @@ "value" : "Gerätekonfiguration empfangen: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración del dispositivo recibida: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -10690,6 +12468,12 @@ "value" : "Gerätekonfiguration" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración del dispositivo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -10736,6 +12520,12 @@ "value" : "Geräte-GPS" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dispositivo GPS" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -10770,6 +12560,12 @@ }, "Device is managed by a mesh administrator, the user is unable to access any of the device settings." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El dispositivo es administrado por un administrador de malla, el usuario no puede acceder a ninguna de las configuraciones del dispositivo." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -10810,6 +12606,12 @@ "value" : "Device Metadata empfangen von: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Metadatos del dispositivo recibidos de: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -10868,6 +12670,12 @@ }, "Device Metrics" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Métricas del dispositivo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -10902,6 +12710,12 @@ }, "Device Metrics Log" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Registro de métricas del dispositivo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -10942,6 +12756,12 @@ "value" : "Gerätemodell: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modelo de dispositivo: %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -10975,10 +12795,23 @@ } }, "Device Options" : { - + "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opciones del dispositivo" + } + } + } }, "Device Role" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Función del dispositivo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -11013,6 +12846,12 @@ }, "Device Screen" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pantalla del dispositivo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -11053,6 +12892,12 @@ "value" : "Gerät, das keine Pakete von anderen Geräten weiterleitet." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dispositivo que no reenvía paquetes desde otros dispositivos." + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -11117,6 +12962,12 @@ "value" : "Gerät, das nur bei Bedarf sendet, um nicht entdeckt zu werden oder Strom zu sparen." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dispositivo que solo transmite según sea necesario para sigilo o ahorro de energía." + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -11175,6 +13026,12 @@ }, "Dilution of precision (DOP) PDOP used by default" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dilución de precisión (DOP) PDOP utilizado por defecto" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -11209,6 +13066,12 @@ "value" : "Direkt" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Directo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -11249,6 +13112,12 @@ "value" : "Hilfe für Direktnachrichten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ayuda por mensaje directo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -11283,6 +13152,12 @@ }, "Direct Message Key" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tecla de mensaje directo" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -11305,6 +13180,12 @@ "value" : "Direktnachrichten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mensajes directos" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -11363,6 +13244,12 @@ }, "Direct messages are using the new public key infrastructure for encryption. Requires firmware version 2.5 or greater." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los mensajes directos utilizan la nueva infraestructura de clave pública para el cifrado. Requiere versión de firmware 2.5 o superior." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -11397,6 +13284,12 @@ }, "Direct messages are using the shared key for the channel." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los mensajes directos utilizan la clave compartida del canal." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -11437,6 +13330,12 @@ "value" : "Deaktiviert" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desactivado" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -11501,6 +13400,12 @@ "value" : "Trennen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desconectar" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -11559,6 +13464,12 @@ }, "Disconnect Node" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desconectar nodo" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -11575,6 +13486,12 @@ }, "Disconnect the currently connected node" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desconectar el nodo actualmente conectado" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -11597,6 +13514,12 @@ "value" : "Tastatur ausblenden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Despedir" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -11661,6 +13584,12 @@ "value" : "Display (Device Screen)" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -11719,6 +13648,12 @@ }, "Display Config" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de pantalla" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -11783,6 +13718,12 @@ "value" : "Display Konfiguration empfangen: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de pantalla recibida: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -11841,6 +13782,12 @@ }, "Display Fahrenheit" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar grados Fahrenheit" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -11875,6 +13822,12 @@ }, "Display Mode" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modo de visualización" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -11915,6 +13868,12 @@ "value" : "Darstellung der Entfernung zwischen deinem Handy und anderen Meshtastic-Knoten mit Positionsangabe." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Muestra la distancia entre tu teléfono y otros nodos Meshtastic con posiciones." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -11925,6 +13884,12 @@ }, "Display Units" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unidades de visualización" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -11965,6 +13930,12 @@ "value" : "Distanz" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Distancia" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -12005,6 +13976,12 @@ "value" : "Distanzfilter" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filtros de distancia" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -12021,6 +13998,12 @@ "value" : "Distanzmessungen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mediciones de distancia" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -12040,6 +14023,12 @@ "value" : "Dokumentation" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Documentación" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -12074,6 +14063,12 @@ }, "Done" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hecho" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -12096,6 +14091,12 @@ }, "Double Tap as Button" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toque dos veces como botón" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -12136,6 +14137,12 @@ "value" : "Runter" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abajo" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -12194,6 +14201,12 @@ }, "Downlink Enabled" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enlace descendente habilitado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -12228,6 +14241,12 @@ }, "Drag & Drop Firmware Update" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actualización de firmware de arrastrar y soltar" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -12262,6 +14281,12 @@ }, "Drag & Drop Firmware Update Documentation" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Documentación de actualización de firmware de arrastrar y soltar" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -12296,6 +14321,12 @@ }, "Drag & Drop is the recommended way to update firmware for NRF devices. If your iPhone or iPad is USB-C it will work with your regular USB-C charging cable, for lightning devices you need the Apple Lightning to USB camera adaptor." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Arrastrar y soltar es la forma recomendada de actualizar el firmware para dispositivos NRF. Si su iPhone o iPad es USB-C, funcionará con su cable de carga USB-C habitual; para dispositivos Lightning, necesita el adaptador de cámara Lightning a USB de Apple." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -12336,6 +14367,12 @@ "value" : "Fahren" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conduciendo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -12370,6 +14407,12 @@ }, "Drop Pin in Maps" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Colocar pin en mapas" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -12404,6 +14447,12 @@ "value" : "Richte einfach private Mesh-Netzwerke für eine sichere und zuverlässige Kommunikation in abgelegenen Gebieten ein." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configure fácilmente redes de malla privadas para una comunicación segura y confiable en áreas remotas." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -12414,6 +14463,12 @@ }, "Echo" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eco" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -12466,6 +14521,12 @@ }, "Editing Waypoint" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Editar punto de referencia" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -12500,6 +14561,12 @@ "value" : "Achtzehn Stunden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dieciocho horas" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -12564,6 +14631,12 @@ "value" : "Höhenunterschied" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elev. Ganar" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -12592,6 +14665,12 @@ }, "Emoji" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "emojis" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -12626,6 +14705,12 @@ }, "Empty" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vacío" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -12659,10 +14744,23 @@ } }, "Enable broadcasting device metrics to the mesh network. When disabled, metrics are only sent to connected clients." : { - + "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Habilite la transmisión de métricas de dispositivos a la red de malla. Cuando está deshabilitado, las métricas solo se envían a los clientes conectados." + } + } + } }, "Enable broadcasting packets via UDP over the local network." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Habilite la transmisión de paquetes a través de UDP a través de la red local." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -12697,6 +14795,12 @@ "value" : "Standortfreigabe aktivieren" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Habilitar compartir ubicación" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -12707,6 +14811,12 @@ }, "Enable Notifications" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Habilitar notificaciones" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -12741,6 +14851,12 @@ }, "Enable this device as a Store and Forward server. Requires an ESP32 device with PSRAM." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Habilite este dispositivo como servidor Store and Forward. Requiere un dispositivo ESP32 con PSRAM." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -12775,6 +14891,12 @@ "value" : "Aktiviert" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activado" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -12839,6 +14961,12 @@ "value" : "Aktiviert automatische TAK-PLI-Übertragungen und verringert die Anzahl der Routineübertragungen." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Permite transmisiones automáticas de TAK PLI y reduce las transmisiones de rutina." + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -12897,6 +15025,12 @@ }, "Enables devices with native I2S audio output to use the RTTTL over speaker like a buzzer. T-Watch S3 and T-Deck for example have this capability." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Permite que los dispositivos con salida de audio I2S nativa utilicen el RTTTL a través del altavoz como un timbre. T-Watch S3 y T-Deck, por ejemplo, tienen esta capacidad." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -12937,6 +15071,12 @@ "value" : "Aktiviert den blauen Standort-Punkt für dein Handy in der Mesh-Karte." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Habilita el punto de ubicación azul para su teléfono en el mapa de malla." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -12947,6 +15087,12 @@ }, "Enables the detection sensor module, it needs to be enabled on both the node with the sensor, and any nodes that you want to receive detection sensor text messages or view the detection sensor log and chart." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Habilita el módulo del sensor de detección; debe estar habilitado tanto en el nodo con el sensor como en cualquier nodo en el que desee recibir mensajes de texto del sensor de detección o ver el registro y el gráfico del sensor de detección." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -12981,6 +15127,12 @@ }, "Enables the store and forward module." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Habilita el módulo de almacenamiento y reenvío." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -13009,6 +15161,12 @@ }, "Enabling Ethernet will disable the bluetooth connection to the app." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Al habilitar Ethernet se deshabilitará la conexión bluetooth a la aplicación." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -13031,6 +15189,12 @@ }, "Enabling WiFi will disable the bluetooth connection to the app." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Habilitar WiFi deshabilitará la conexión bluetooth a la aplicación." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -13041,6 +15205,12 @@ }, "Encoder Press Event" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Evento de prensa del codificador" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -13075,6 +15245,12 @@ "value" : "Verschlüsselt" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "cifrado" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -13140,6 +15316,12 @@ "value" : "Verschlüsseltes Senden fehlgeschlagen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error de envío cifrado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -13168,6 +15350,12 @@ }, "Encryption Enabled" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cifrado habilitado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -13208,6 +15396,12 @@ "value" : "DFÜ-Modus aktivieren" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingrese al modo DFU" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -13244,6 +15438,12 @@ "comment" : "A label for a text field where the user can enter a hostname or IP address and optionally a port number.", "isCommentAutoGenerated" : true, "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Introduzca el nombre de host[:puerto]" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -13260,6 +15460,12 @@ "value" : "Umgebung" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "ambiente" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -13300,6 +15506,12 @@ "value" : "Umgebung" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ambiente" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -13334,6 +15546,12 @@ }, "Environment Metrics" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Métricas ambientales" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -13367,10 +15585,23 @@ } }, "Environment Metrics Enabled" : { - + "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Métricas de entorno habilitadas" + } + } + } }, "Environment Metrics Log" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Registro de métricas ambientales" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -13404,7 +15635,14 @@ } }, "Environment Sensor Options" : { - + "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opciones de sensores ambientales" + } + } + } }, "Erase all app data?" : { "localizations" : { @@ -13414,6 +15652,12 @@ "value" : "Alle App-Daten löschen?" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Borrar todos los datos de la aplicación?" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -13454,6 +15698,12 @@ "value" : "Alle Geräte- und App-Daten löschen?" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Borrar todos los datos del dispositivo y de las aplicaciones?" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -13488,6 +15738,12 @@ }, "Error: %@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error: %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -13522,6 +15778,12 @@ }, "ESP 32 OTA update is a work in progress, click the button below to send your device a reboot into ota admin message." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La actualización de ESP 32 OTA es un trabajo en progreso, haga clic en el botón a continuación para enviar su dispositivo a un reinicio en el mensaje de administrador de ota." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -13556,6 +15818,12 @@ }, "ESP32 Device Firmware Update" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actualización del firmware del dispositivo ESP32" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -13590,6 +15858,12 @@ }, "Ethernet Options" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opciones de Ethernet" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -13618,6 +15892,12 @@ }, "European Union 433MHz" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unión Europea 433MHz" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -13646,6 +15926,12 @@ }, "European Union 868MHz" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unión Europea 868MHz" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -13680,6 +15966,12 @@ "value" : "Abend" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Noche" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -13708,6 +16000,12 @@ }, "Exchange Positions" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Posiciones de intercambio" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -13742,6 +16040,12 @@ "value" : "Ausrufezeichen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exclamación" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -13800,6 +16104,12 @@ }, "Expiration" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vencimiento" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -13822,6 +16132,12 @@ "value" : "Zeitpunkt" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Expirar" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -13856,6 +16172,12 @@ "value" : "Automatisches Löschen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vence" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -13884,6 +16206,12 @@ }, "Expires: %@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vence: %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -13918,6 +16246,12 @@ "value" : "Exportieren" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exportar" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -13952,6 +16286,12 @@ "value" : "Externe Benachrichtigung" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notificación externa" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -14016,6 +16356,12 @@ "value" : "Einstellungen der externen Benachrichtigung" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de notificación externa" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -14074,6 +16420,12 @@ }, "External Notification module config received: %@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración del módulo de notificación externa recibida: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -14138,6 +16490,12 @@ "value" : "Werkseinstellungen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restablecimiento de fábrica" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -14166,6 +16524,12 @@ }, "Factory reset will delete device and app data." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El restablecimiento de fábrica eliminará los datos del dispositivo y de la aplicación." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -14188,6 +16552,12 @@ }, "Failed to encode message content" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se pudo codificar el contenido del mensaje" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -14216,6 +16586,12 @@ }, "Failed to get a valid position to exchange" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se pudo obtener una posición válida para intercambiar" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -14244,6 +16620,12 @@ }, "Failed to get a valid position to exchange." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se pudo obtener una posición válida para intercambiar." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -14278,6 +16660,12 @@ "value" : "Ordentliche Signalstärke" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Justo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -14312,6 +16700,12 @@ "value" : "Favorit" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Favorito" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -14346,6 +16740,12 @@ "value" : "Knoten, die als Favorit markiert oder ignoriert wurden, bleiben immer erhalten. Knoten ohne PKC-Schlüssel werden gemäß dem festgelegten Zeitplan aus der App-Datenbank gelöscht. Knoten mit PKC-Schlüsseln werden nur gelöscht, wenn das Intervall auf 7 Tage oder länger eingestellt ist. Diese Funktion löscht nur Knoten aus der App, die nicht in der Geräteknoten-Datenbank gespeichert sind." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los nodos favoritos e ignorados siempre se conservan. Los nodos sin claves PKC se borran de la base de datos de la aplicación según el cronograma establecido por el usuario, los nodos con claves PKC se borran solo si el intervalo se establece en 7 días o más. Esta función solo elimina los nodos de la aplicación que no están almacenados en la base de datos de nodos del dispositivo." + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -14368,6 +16768,12 @@ "value" : "Favoriten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Favoritos" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -14402,6 +16808,12 @@ "value" : "Favoriten und Knoten mit aktuellen Nachrichten werden oben in der Kontaktliste angezeigt." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los favoritos y los nodos con mensajes recientes aparecen en la parte superior de la lista de contactos." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -14442,6 +16854,12 @@ "value" : "Letzte Position eines Knotens holen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obtener la última posición de un nodo determinado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -14470,6 +16888,12 @@ }, "Fifteen Minutes" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "quince minutos" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -14504,6 +16928,12 @@ "value" : "Fünfzehn Sekunden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "quince segundos" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -14562,6 +16992,12 @@ }, "File Storage" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Almacenamiento de archivos" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -14591,6 +17027,12 @@ "Files Available" : { "comment" : "Data source label when files exist but none are active", "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Archivos disponibles" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -14607,6 +17049,12 @@ "value" : "Filtere die Knotenliste und die Mesh-Karte nach der Nähe zu deinem Handy." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filtre la lista de nodos y el mapa de malla según la proximidad a su teléfono." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -14623,6 +17071,12 @@ "value" : "Kontakt suchen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "encontrar un contacto" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -14663,6 +17117,12 @@ "value" : "Einen Knoten finden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Encuentra un nodo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -14703,6 +17163,12 @@ "value" : "Beenden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Finalizar" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -14768,6 +17234,12 @@ "value" : "Ziel" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Finalizar" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -14784,6 +17256,12 @@ "value" : "Firmware" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "firmware" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -14818,6 +17296,12 @@ }, "Firmware update docs" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Documentos de actualización de firmware" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -14858,6 +17342,12 @@ "value" : "Firmwareaktualisierungen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actualizaciones de firmware" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -14898,6 +17388,12 @@ "value" : "Firmware Version" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versión de firmware" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -14956,6 +17452,12 @@ }, "First heard" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "escuchado por primera vez" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -14996,6 +17498,12 @@ "value" : "Fünf Stunden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "cinco horas" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -15060,6 +17568,12 @@ "value" : "Fünf Minuten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "cinco minutos" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -15094,6 +17608,12 @@ "value" : "Fünf Sekunden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "cinco segundos" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -15158,6 +17678,12 @@ "value" : "Feste PIN" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pasador fijo" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -15216,6 +17742,12 @@ }, "Fixed Position" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Posición fija" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -15244,6 +17776,12 @@ }, "Flip Screen" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voltear pantalla" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -15272,6 +17810,12 @@ }, "Flip screen vertically" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voltear la pantalla verticalmente" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -15306,6 +17850,12 @@ "value" : "Folgen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seguir" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -15370,6 +17920,12 @@ "value" : "Folgen mit Steuerkurs" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seguir con encabezado" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -15428,6 +17984,12 @@ }, "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." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Para todas las funciones de Mqtt además del informe de mapa, también debe configurar el enlace ascendente y descendente para cada canal que desee conectar a través de Mqtt." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -15468,6 +18030,12 @@ "value" : "Für alle" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Para todos" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -15502,6 +18070,12 @@ "value" : "Für mich" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Para mí" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -15536,6 +18110,12 @@ "value" : "Achtundvierzig Stunden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "cuarenta y ocho horas" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -15600,6 +18180,12 @@ "value" : "Fündundvierzig Sekunden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "cuarenta y cinco segundos" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -15664,6 +18250,12 @@ "value" : "Vier Stunden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "cuatro horas" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -15728,6 +18320,12 @@ "value" : "Vier Sekunden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "cuatro segundos" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -15792,6 +18390,12 @@ "value" : "Frequenz" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Frecuencia" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -15820,6 +18424,12 @@ }, "Frequency Override" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anulación de frecuencia" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -15848,6 +18458,12 @@ }, "Frequency Slot" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ranura de frecuencia" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -15876,6 +18492,12 @@ }, "Friendly name" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre amigable" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -15910,6 +18532,12 @@ }, "Friendly name used to format message sent to mesh. Example: A name \"Motion\" would result in a message \"Motion detected\"" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre descriptivo utilizado para formatear el mensaje enviado a la malla. Ejemplo: un nombre \"Movimiento\" daría como resultado un mensaje \"Movimiento detectado\"." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -15944,6 +18572,12 @@ }, "From Radio (RX): %lld" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desde radio (RX): %lld" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -15954,6 +18588,12 @@ }, "Full Support" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Soporte completo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -15988,6 +18628,12 @@ }, "Generate a new private key to replace the one currently in use. The public key will automatically be regenerated from your private key." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Genere una nueva clave privada para reemplazar la que está actualmente en uso. La clave pública se regenerará automáticamente a partir de su clave privada." + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -16010,6 +18656,12 @@ "value" : "QR Code Erzeugen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Generar código QR" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -16068,6 +18720,12 @@ }, "Get custom waterproof solar and detection sensor router nodes, aluminium desktop nodes and rugged handsets." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obtenga nodos de enrutador de sensores de detección y solares impermeables personalizados, nodos de escritorio de aluminio y teléfonos resistentes." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -16102,6 +18760,12 @@ "value" : "Knotenposition ermitteln" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obtener la posición del nodo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -16136,6 +18800,12 @@ }, "Get NRF DFU from the App Store" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obtenga NRF DFU en la App Store" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -16176,6 +18846,12 @@ "value" : "Los geht's" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "empezar" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -16186,6 +18862,12 @@ }, "Get the latest stable firmware" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obtenga el firmware estable más reciente" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -16220,6 +18902,12 @@ }, "GitHub Repository" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Repositorio GitHub" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -16236,6 +18924,12 @@ }, "Good" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bien" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -16264,6 +18958,12 @@ }, "GPIO" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "GPIO" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -16298,6 +18998,12 @@ }, "GPIO Output Duration" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Duración de la salida GPIO" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -16326,6 +19032,12 @@ }, "GPIO pin for rotary encoder A port." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pin GPIO para el puerto A del codificador rotatorio." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -16354,6 +19066,12 @@ }, "GPIO pin for rotary encoder B port." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pin GPIO para el puerto B del codificador rotatorio." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -16382,6 +19100,12 @@ }, "GPIO pin for rotary encoder Press port." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pin GPIO para codificador rotatorio Puerto de prensa." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -16410,6 +19134,12 @@ }, "GPIO Pin to monitor" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pin GPIO para monitorear" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -16438,6 +19168,12 @@ }, "GPS EN GPIO" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "GPS EN GPIO" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -16472,6 +19208,12 @@ }, "GPS Receive GPIO" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recepción GPS GPIO" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -16506,6 +19248,12 @@ }, "GPS Transmit GPIO" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Transmisión GPS GPIO" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -16546,6 +19294,12 @@ "value" : "Gruppennachricht" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mensaje grupal" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -16580,6 +19334,12 @@ }, "Gusts %@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ráfagas %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -16608,6 +19368,12 @@ }, "HaHa" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ja ja" + } + }, "he" : { "stringUnit" : { "state" : "translated", @@ -16648,6 +19414,12 @@ }, "Hard Reset" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restablecimiento completo" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -16658,6 +19430,12 @@ }, "Hardware" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hardware" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -16692,6 +19470,12 @@ }, "Hazardous" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Peligroso" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -16720,6 +19504,12 @@ }, "Heading" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Título" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -16754,6 +19544,12 @@ "value" : "Kurs: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Título: %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -16788,6 +19584,12 @@ "value" : "Gehört" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Escuchó" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -16852,6 +19654,12 @@ "value" : "Herz" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Corazón" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -16910,6 +19718,12 @@ }, "Hide alerts" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ocultar alertas" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -16944,6 +19758,12 @@ }, "Hide Alerts" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ocultar alertas" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -16978,6 +19798,12 @@ "value" : "HOCH" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "ALTO" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -17018,6 +19844,12 @@ "value" : "Wandern" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Senderismo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -17052,6 +19884,12 @@ }, "History Return Max" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Historial Retorno Max" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -17080,6 +19918,12 @@ }, "History Return Window" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ventana de retorno del historial" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -17114,6 +19958,12 @@ "value" : "Hops Entfernt" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "salta lejos" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -17148,6 +19998,12 @@ "value" : "Hops Entfernt %d" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Salta lejos %d" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -17182,6 +20038,12 @@ "value" : "Hops Entfernt:" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saltos lejos:" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -17216,6 +20078,12 @@ "value" : "Hops Entfernt: %d" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saltos lejos: %d" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -17250,6 +20118,12 @@ "value" : "Stunde" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hora" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -17284,6 +20158,12 @@ }, "Hourly Duty Cycle" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ciclo de trabajo por hora" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -17312,6 +20192,12 @@ }, "How long the screen remains on after the user button is pressed or messages are received." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cuánto tiempo permanece encendida la pantalla después de presionar el botón de usuario o recibir mensajes." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -17346,6 +20232,12 @@ }, "How often device metrics are sent out over the mesh. Default is 30 minutes." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Con qué frecuencia se envían las métricas del dispositivo a través de la malla. El valor predeterminado es 30 minutos." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -17380,6 +20272,12 @@ }, "How often environment metrics are sent out over the mesh. Default is 30 minutes." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Con qué frecuencia se envían métricas ambientales a través de la malla. El valor predeterminado es 30 minutos." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -17414,6 +20312,12 @@ }, "How often power metrics are sent out over the mesh. Default is 30 minutes." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Con qué frecuencia se envían métricas de potencia a través de la malla. El valor predeterminado es 30 minutos." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -17448,6 +20352,12 @@ }, "How often should we try to get a GPS position." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Con qué frecuencia debemos intentar obtener una posición GPS?" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -17482,6 +20392,12 @@ }, "How often to send detection sensor state to mesh regardless of detection. Default is Never." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Con qué frecuencia enviar el estado del sensor de detección a la malla independientemente de la detección. El valor predeterminado es Nunca." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -17522,6 +20438,12 @@ "value" : "How often we can send a message to the mesh when people are detected." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Con qué frecuencia podemos enviar un mensaje a la malla cuando se detectan personas." + } + }, "he" : { "stringUnit" : { "state" : "translated", @@ -17580,6 +20502,12 @@ "value" : "Wie wird die Firmware aktualisiert" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cómo actualizar el firmware" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -17614,6 +20542,12 @@ }, "Hum" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tararear" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -17648,6 +20582,12 @@ "value" : "Luftfeuchtigkeit" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Humedad" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -17676,6 +20616,12 @@ }, "Hybrid" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Híbrido" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -17734,6 +20680,12 @@ }, "Hybrid Flyover" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paso elevado híbrido" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -17792,6 +20744,12 @@ }, "I have read and understand the above. I voluntarily consent to the unencrypted transmission of my node data via MQTT." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "He leído y entiendo lo anterior. Doy mi consentimiento voluntariamente para la transmisión sin cifrar de los datos de mi nodo a través de MQTT." + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -17814,6 +20772,12 @@ }, "IAQ" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "IAQ" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -17848,6 +20812,12 @@ }, "IAQ " : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "IAQ" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -17882,6 +20852,12 @@ }, "IAQ %lld" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "IAQ %lld" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -17922,6 +20898,12 @@ "value" : "Emoji" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Icono" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -17956,6 +20938,12 @@ }, "Icons" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Iconos" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -17966,6 +20954,12 @@ }, "If DOP is set, use HDOP / VDOP values instead of PDOP" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Si se configura DOP, use valores HDOP/VDOP en lugar de PDOP" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -18000,6 +20994,12 @@ }, "If enabled, the 'output' Pin will be pulled active high, disabled means active low." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Si está habilitado, el pin de 'salida' se activará alto, deshabilitado significa activo bajo." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -18034,6 +21034,12 @@ }, "If it is hard to access your device's reset button enter DFU mode here." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Si le resulta difícil acceder al botón de reinicio de su dispositivo, ingrese al modo DFU aquí." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -18068,6 +21074,12 @@ }, "If set, any packets you send will be echoed back to your device." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Si está configurado, cualquier paquete que envíe se enviará a su dispositivo." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -18102,6 +21114,12 @@ }, "If the default region topic is too busy you can choose a more local topic." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Si el tema de la región predeterminada está demasiado ocupado, puede elegir un tema más local." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -18136,6 +21154,12 @@ }, "Ignore MQTT" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignorar MQTT" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -18170,6 +21194,12 @@ }, "Ignore Node" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignorar nodo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -18204,6 +21234,12 @@ }, "Ignored" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "ignorado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -18238,6 +21274,12 @@ }, "Ignores observed messages from foreign meshes like Local Only, but takes it step further by also ignoring messages from nodes not already in the node's known list." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignora los mensajes observados de mallas externas como Solo local, pero va un paso más allá al ignorar también los mensajes de nodos que aún no están en la lista conocida del nodo." + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -18260,6 +21302,12 @@ }, "Ignores observed messages from foreign meshes that are open or those which it cannot decrypt. Only rebroadcasts message on the nodes local primary / secondary channels." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignora los mensajes observados de mallas externas que están abiertas o aquellas que no puede descifrar. Sólo retransmite mensajes en los canales primarios/secundarios locales del nodo." + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -18288,6 +21336,12 @@ "value" : "Route importieren" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ruta de importación" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -18322,6 +21376,12 @@ }, "In addition to Config, Keys and BLE bonds will be wiped" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Además de la configuración, se borrarán las claves y los enlaces BLE." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -18338,6 +21398,12 @@ "value" : "Include" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Incluir" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -18402,6 +21468,12 @@ "value" : "Eingehende Nachrichten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mensajes entrantes" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -18418,6 +21490,12 @@ "value" : "Unvollständig" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Incompleto" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -18452,6 +21530,12 @@ }, "India" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "India" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -18474,6 +21558,12 @@ }, "Indoor Air Quality" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Calidad del aire interior" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -18508,6 +21598,12 @@ }, "Indoor Air Quality (IAQ)" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Calidad del aire interior (IAQ)" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -18548,6 +21644,12 @@ "value" : "Router - Mesh Pakete werden bevorzugt über diesen Knoten gerouted. Dieser Knoten wird nicht von einer Client App benutzt. WLAN, Bluetooth und Display sind aus." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nodo de infraestructura únicamente en una torre o cima de una montaña. No debe usarse para techos o nodos móviles. Necesita una cobertura excepcional. Visible en la lista de nodos." + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -18606,6 +21708,12 @@ }, "Inputs" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Entradas" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -18646,6 +21754,12 @@ "value" : "Ungültiger Dateiinhalt. Bitte überprüfe das Dateiformat." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contenido del archivo no válido. Por favor verifique el formato del archivo." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -18656,6 +21770,12 @@ }, "Inverted top bar for 2 Color display" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Barra superior invertida para pantalla de 2 colores" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -18690,6 +21810,12 @@ }, "Japan" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Japón" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -18718,6 +21844,12 @@ }, "JSON Enabled" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "JSON habilitado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -18752,6 +21884,12 @@ }, "JSON mode is a limited, unencrypted MQTT output for locally integrating with home assistant" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El modo JSON es una salida MQTT limitada y sin cifrar para la integración local con el asistente doméstico" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -18786,6 +21924,12 @@ }, "Jump to present" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saltar al presente" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -18814,6 +21958,12 @@ "value" : "Schlüssel" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Llave" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -18848,6 +21998,12 @@ }, "Key Backup" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copia de seguridad clave" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -18864,6 +22020,12 @@ }, "Key Mapping" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mapeo de claves" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -18898,6 +22060,12 @@ "value" : "Schlüsselgröße" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tamaño de clave" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -18926,6 +22094,12 @@ }, "Korea" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Corea" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -18960,6 +22134,12 @@ "value" : "Zuletzt gehört" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Escuchado por última vez" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -19007,6 +22187,12 @@ "value" : "Breitengrad" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Latitud" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -19041,6 +22227,12 @@ }, "Latitude in degrees (e.g., 37.7749)" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Latitud en grados (por ejemplo, 37,7749)" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -19057,6 +22249,12 @@ }, "Latitude must be between -90 and 90 degrees" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La latitud debe estar entre -90 y 90 grados." + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -19073,6 +22271,12 @@ }, "LED Heartbeat" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Latido del corazón LED" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -19113,6 +22317,12 @@ "value" : "LED Status" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estado del LED" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -19153,6 +22363,12 @@ "value" : "Links" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Izquierda" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -19217,6 +22433,12 @@ "value" : "Level" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nivel" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -19275,6 +22497,12 @@ }, "Licensed Operator" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Operador Licenciado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -19309,6 +22537,12 @@ }, "Limit all periodic broadcast intervals especially telemetry and position. If you need to increase hops, do it on nodes at the edges, not the ones in the middle. MQTT is not advised when you are duty cycle restricted because the gateway node is then doing all the work." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Limite todos los intervalos de transmisión periódica, especialmente la telemetría y la posición. Si necesita aumentar los saltos, hágalo en los nodos de los bordes, no en los del medio. No se recomienda MQTT cuando el ciclo de trabajo está restringido porque el nodo de puerta de enlace está haciendo todo el trabajo." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -19343,6 +22577,12 @@ }, "Line Series" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Serie de línea" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -19371,6 +22611,12 @@ }, "Loading Logs. . ." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cargando registros. . ." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -19407,6 +22653,12 @@ "comment" : "A label displayed above the options for local network access.", "isCommentAutoGenerated" : true, "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Acceso a la red local" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -19423,6 +22675,12 @@ "value" : "Standort:" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ubicación:" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -19463,6 +22721,12 @@ "value" : "Gesperrt" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "bloqueado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -19497,6 +22761,12 @@ }, "Log Levels" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Niveles de registro" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -19537,6 +22807,12 @@ "value" : "Logging" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Explotación florestal" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -19595,6 +22871,12 @@ }, "Logs" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Registros" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -19629,6 +22911,12 @@ }, "Logs:" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Registros:" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -19669,6 +22957,12 @@ "value" : "Langer Name" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre largo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -19709,6 +23003,12 @@ "value" : "Durch langes Gedrückthalten kannst du den Kontakt zu deinen Favoriten hinzufügen, stumm schalten oder eine Unterhaltung löschen." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mantenga presionado para marcar como favorito, silenciar el contacto o eliminar una conversación." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -19743,6 +23043,12 @@ }, "Long Range - Fast" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Largo alcance - Rápido" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -19771,6 +23077,12 @@ }, "Long Range - Moderate" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Largo alcance - Moderado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -19799,6 +23111,12 @@ }, "Long Range - Slow" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Largo alcance - Lento" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -19833,6 +23151,12 @@ "value" : "Längengrad" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Longitud" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -19867,6 +23191,12 @@ }, "Longitude in degrees (e.g., -122.4194)" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Longitud en grados (p. ej., -122,4194)" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -19883,6 +23213,12 @@ }, "Longitude must be between -180 and 180 degrees" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La longitud debe estar entre -180 y 180 grados." + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -19905,6 +23241,12 @@ "value" : "LoRa" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "lora" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -19969,6 +23311,12 @@ "value" : "LoRa Einstellungen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración LoRa" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -20027,6 +23375,12 @@ }, "LoRa Config Changes:" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cambios en la configuración de LoRa:" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -20043,6 +23397,12 @@ "value" : "LoRa config empfangen: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de LoRa recibida: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -20107,6 +23467,12 @@ "value" : "Tracker" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Objetos perdidos y encontrados" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -20141,6 +23507,12 @@ }, "LOW" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "BAJO" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -20175,6 +23547,12 @@ "value" : "Niedriger Akkustand" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Batería baja" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -20191,6 +23569,12 @@ "value" : "M5 Stack Card KB / RAK Tastenfeld" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tarjeta de pila M5 Teclado KB / RAK" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -20231,6 +23615,12 @@ }, "Malaysia 433MHz" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Malasia 433MHz" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -20259,6 +23649,12 @@ }, "Malaysia 919MHz" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Malasia 919MHz" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -20293,6 +23689,12 @@ "value" : "Kanäle verwalten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Administrar canales" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -20352,6 +23754,12 @@ "Manage custom map overlays" : { "comment" : "Subtitle for map data management", "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Administrar superposiciones de mapas personalizados" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -20393,6 +23801,12 @@ "value" : "Kartendaten verwalten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Administrar datos de mapas" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -20403,6 +23817,12 @@ }, "Managed Device" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dispositivo administrado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -20437,6 +23857,12 @@ }, "Manual" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manual" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -20459,6 +23885,12 @@ "value" : "Manuelle Konfiguration" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración manual" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -20517,6 +23949,12 @@ }, "Manual connection string" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cadena de conexión manual" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -20530,6 +23968,12 @@ }, "Map Data" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datos del mapa" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -20546,6 +23990,12 @@ "value" : "Kartenoptionen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opciones de mapa" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -20586,6 +24036,12 @@ "value" : "Karten-Overlays" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Superposiciones de mapas" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -20644,6 +24100,12 @@ }, "Map Publish Interval" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intervalo de publicación de mapas" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -20678,6 +24140,12 @@ }, "Map Report" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Informe de mapa" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -20718,6 +24186,12 @@ "value" : "Maximale Wiederholungen erreicht" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retransmisión máxima alcanzada" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -20776,6 +24250,12 @@ }, "Medium Range - Fast" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rango medio - Rápido" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -20804,6 +24284,12 @@ }, "Medium Range - Slow" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rango medio - Lento" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -20832,6 +24318,12 @@ }, "Mesh activity update" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actualización de actividad de malla" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -20866,6 +24358,12 @@ "value" : "Mesh Live Aktivität" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actividad en vivo de malla" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -20930,6 +24428,12 @@ "value" : "Mesh Karte" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mapa de malla" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -20994,6 +24498,12 @@ "value" : "Standort auf der Mesh-Karte" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ubicación del mapa de malla" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -21004,6 +24514,12 @@ }, "Meshtastic" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Meshtástico" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -21023,6 +24539,12 @@ "value" : "Meshtastic Knoten %@ hat Kanäle mit dir geteilt" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Meshtastic Node %@ ha compartido canales contigo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -21057,6 +24579,12 @@ "value" : "Meshtastic verwendet den Standort deines Handys, um eine Reihe von Funktionen zu ermöglichen. Du kannst deine Standortberechtigungen jederzeit in den Einstellungen aktualisieren." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Meshtastic utiliza la ubicación de su teléfono para habilitar una serie de funciones. Puede actualizar sus permisos de ubicación en cualquier momento desde la configuración." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -21067,6 +24595,12 @@ }, "Meshtastic® Copyright Meshtastic LLC" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Meshtastic® Copyright Meshtastic LLC" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -21101,6 +24635,12 @@ "value" : "Nachricht" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mensaje" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -21135,6 +24675,12 @@ "value" : "Nachrichteninhalt überschreitet 200 Bytes." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El contenido del mensaje supera los 200 bytes." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -21169,6 +24715,12 @@ "value" : "Nachrichtendetails" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Detalles del mensaje" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -21233,6 +24785,12 @@ "value" : "Nachricht von der Textnachricht-App empfangen." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mensaje recibido de la aplicación de mensajes de texto." + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -21292,6 +24850,12 @@ "Message Size" : { "comment" : "VoiceOver label for message size", "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tamaño del mensaje" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -21308,6 +24872,12 @@ }, "Message Status Options" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opciones de estado del mensaje" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -21342,6 +24912,12 @@ "value" : "Nachrichten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mensajes" + } + }, "he" : { "stringUnit" : { "state" : "translated", @@ -21400,6 +24976,12 @@ "value" : "Nachrichten getrennt mit |" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los mensajes se separan con |" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -21428,6 +25010,12 @@ }, "Messaging" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mensajería" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -21444,6 +25032,12 @@ }, "Metric" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Métrico" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -21478,6 +25072,12 @@ "value" : "Mittag" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uno" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -21512,6 +25112,12 @@ "value" : "Minimum Distanz" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Distancia mínima" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -21546,6 +25152,12 @@ "value" : "Minimum Intervall" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intervalo mínimo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -21574,6 +25186,12 @@ }, "Minimum time between detection broadcasts" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tiempo mínimo entre transmisiones de detección" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -21602,6 +25220,12 @@ }, "Mininum time between detection broadcasts. Default is 45 seconds." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tiempo mínimo entre transmisiones de detección. El valor predeterminado es 45 segundos." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -21636,6 +25260,12 @@ "value" : "Modus" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modo" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -21694,6 +25324,12 @@ }, "Model" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modelo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -21728,6 +25364,12 @@ }, "Moderate" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Moderado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -21768,6 +25410,12 @@ "value" : "Modul Konfiguration" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración del módulo" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -21832,6 +25480,12 @@ "value" : "Morgen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mañana" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -21866,6 +25520,12 @@ "value" : "Die meisten Daten in deinem Mesh werden über den primären Kanal gesendet. Du kannst sekundäre Kanäle einrichten, um zusätzliche Nachrichtengruppen zu erstellen, die durch ihren eigenen Schlüssel gesichert sind. [Tipps zur Kanalkonfiguration](https://meshtastic.org/docs/configuration/radio/channels/)" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La mayoría de los datos de su malla se envían a través del canal principal. Puede configurar canales secundarios para crear grupos de mensajería adicionales protegidos por su propia clave. [Consejos de configuración de canales](https://meshtastic.org/docs/configuration/tips/)" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -21924,6 +25584,12 @@ }, "MQTT" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "MQTT" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -21964,6 +25630,12 @@ "value" : "MQTT Client Proxy" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Proxy de cliente MQTT" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -22028,6 +25700,12 @@ "value" : "MQTT Konfiguration" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración MQTT" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -22092,6 +25770,12 @@ "value" : "MQTT Modulkonfiguration empfangen: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración del módulo MQTT recibida: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -22156,6 +25840,12 @@ "value" : "Multiplier" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Multiplicador" + } + }, "he" : { "stringUnit" : { "state" : "translated", @@ -22202,6 +25892,12 @@ }, "Must be a single emoji" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debe ser un solo emoji" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -22236,6 +25932,12 @@ "value" : "MyInfo empfangen: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mi información recibida: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -22295,6 +25997,12 @@ "Nag timeout" : { "extractionState" : "stale", "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se acabó el tiempo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -22322,7 +26030,14 @@ } }, "Nag Timeout" : { - + "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tiempo agotado" + } + } + } }, "Name" : { "localizations" : { @@ -22332,6 +26047,12 @@ "value" : "Name" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -22372,6 +26093,12 @@ "value" : "Name muss kürzer als 30 Bytes sein" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El nombre debe tener menos de 30 bytes." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -22406,6 +26133,12 @@ }, "Navigate to node" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Navegar al nodo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -22434,6 +26167,12 @@ }, "Nearby Topics" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temas cercanos" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -22468,6 +26207,12 @@ "value" : "Netzwerk" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Red" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -22532,6 +26277,12 @@ "value" : "Netzwerkeinstellungen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de red" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -22596,6 +26347,12 @@ "value" : "Netzwerkkonfiguration empfangen: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de red recibida: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -22654,6 +26411,12 @@ }, "Network Status Orange" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estado de la red naranja" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -22688,6 +26451,12 @@ }, "Network Status Red" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estado de la red Rojo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -22722,6 +26491,12 @@ }, "New Node" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nuevo nodo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -22750,6 +26525,12 @@ }, "New Node has been discovered" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se ha descubierto un nuevo nodo." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -22784,6 +26565,12 @@ "value" : "Neue Knoten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nuevos nodos" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -22794,6 +26581,12 @@ }, "New Zealand 865MHz" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nueva Zelanda 865MHz" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -22828,6 +26621,12 @@ "value" : "Neuere Firmware ist verfügbar" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hay un firmware más nuevo disponible" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -22868,6 +26667,12 @@ "value" : "Nacht" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Noche" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -22902,6 +26707,12 @@ "value" : "NMEA Positionen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Posiciones NMEA" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -22966,6 +26777,12 @@ "value" : "Kein Kanal" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin canal" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -23030,6 +26847,12 @@ "value" : "Kein verbundener Knoten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ningún nodo conectado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -23065,6 +26888,12 @@ "value" : "Keine Daten vorhanden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin datos" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -23105,6 +26934,12 @@ "value" : "Kein Gerät verbunden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ningún dispositivo conectado" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -23163,6 +26998,12 @@ }, "No Device Metrics" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin métricas de dispositivo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -23191,6 +27032,12 @@ }, "No Environment Metrics" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin métricas ambientales" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -23231,6 +27078,12 @@ "value" : "Keine Dateien hochgeladen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se subieron archivos" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -23247,6 +27100,12 @@ "value" : "Keine Schnittstelle" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin interfaz" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -23306,6 +27165,12 @@ "No map data files uploaded" : { "comment" : "Message when no files are uploaded", "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se han subido archivos de datos de mapas" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -23316,6 +27181,12 @@ }, "No PAX Counter Logs" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin registros de contador de PAX" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -23356,6 +27227,12 @@ "value" : "Keine PIN (geht einfach)" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin PIN (simplemente funciona)" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -23420,6 +27297,12 @@ "value" : "Keine Positionen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin posiciones" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -23448,6 +27331,12 @@ }, "No Power Metrics" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin métricas de energía" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -23482,6 +27371,12 @@ "value" : "Keine Antwort" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin respuesta" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -23546,6 +27441,12 @@ "value" : "Keine Route" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin ruta" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -23610,6 +27511,12 @@ "value" : "Knoten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nodo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -23644,6 +27551,12 @@ "value" : "Node Core Data Backup %1$@/%2$@ - %3$@ - %4$@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copia de seguridad de datos principales del nodo %@/%@ - %@ - %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -23684,6 +27597,12 @@ "value" : "Knoten hat keine Position" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El nodo no tiene posiciones" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -23718,6 +27637,12 @@ "value" : "Knoten Historie" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Historia del nodo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -23746,6 +27671,12 @@ }, "Node Info Broadcast Interval" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intervalo de transmisión de información de nodo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -23780,6 +27711,12 @@ "value" : "Knotenkarte" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mapa de nodos" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -23814,6 +27751,12 @@ "value" : "Knotennummer" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Número de nodo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -23854,6 +27797,12 @@ "value" : "Knoten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nodos" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -23912,6 +27861,12 @@ "value" : "Knoten (%@)" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nodos (%@)" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -23976,6 +27931,12 @@ "value" : "Keins" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ninguno" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -24034,6 +27995,12 @@ }, "Not a valid route file" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No es un archivo de ruta válido" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -24068,6 +28035,12 @@ "value" : "Nicht authorisiert" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No autorizado" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -24124,8 +28097,24 @@ } } }, + "Not Connected" : { + "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No conectado" + } + } + } + }, "Not Present" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No presente" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -24184,6 +28173,12 @@ "value" : "Knoten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notas" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -24218,6 +28213,12 @@ "value" : "Mitteilungen für Kanal- und Direktnachrichten." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notificaciones por canal y mensajes directos." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -24234,6 +28235,12 @@ "value" : "Mitteilungen bei niedrigem Akkustand des verbundenen Funkgeräts." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notificaciones de alertas de batería baja para el dispositivo conectado." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -24250,6 +28257,12 @@ "value" : "Mitteilungen für neu entdeckte Knoten." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notificaciones para nodos recién descubiertos." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -24266,6 +28279,12 @@ "value" : "Anzahl Hops" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Número de saltos" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -24300,6 +28319,12 @@ "value" : "Anzahl Einträge" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Número de registros" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -24334,6 +28359,12 @@ "value" : "Anzahl Satelliten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Número de satélites" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -24362,6 +28393,12 @@ }, "Ok" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "De acuerdo" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -24378,6 +28415,12 @@ "value" : "Ok" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "DE ACUERDO" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -24406,6 +28449,12 @@ }, "Ok to MQTT" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ok para MQTT" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -24446,6 +28495,12 @@ "value" : "OLED Typ" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tipo OLED" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -24486,6 +28541,12 @@ "value" : "Nur beim Starten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sólo en el arranque" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -24544,6 +28605,12 @@ }, "Onboarding for licensed operators requires firmware 2.0.20 or greater. Make sure to refer to your local regulations and contact the local amateur frequency coordinators with questions." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La incorporación de operadores con licencia requiere firmware 2.0.20 o superior. Asegúrese de consultar las regulaciones locales y comuníquese con los coordinadores locales de frecuencias de aficionados si tiene preguntas." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -24584,6 +28651,12 @@ "value" : "Eine Stunde" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Una hora" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -24648,6 +28721,12 @@ "value" : "Eine Minute" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "un minuto" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -24712,6 +28791,12 @@ "value" : "Eine Sekunde" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "un segundo" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -24776,6 +28861,12 @@ "value" : "Online" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "En línea" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -24804,6 +28895,12 @@ }, "Only permitted for SENSOR, TRACKER and TAK_TRACKER roles, this will inhibit all rebroadcasts, not unlike CLIENT_MUTE role." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Solo permitido para los roles SENSOR, TRACKER y TAK_TRACKER, esto inhibirá todas las retransmisiones, al igual que el rol CLIENT_MUTE." + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -24826,6 +28923,12 @@ }, "Only rebroadcasts packets from the core portnums: NodeInfo, Text, Position, Telemetry, and Routing." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Solo retransmite paquetes desde los portnums principales: NodeInfo, Texto, Posición, Telemetría y Enrutamiento." + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -24857,6 +28960,12 @@ "value" : "Einstellungen öffnen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abrir configuración" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -24891,6 +29000,12 @@ }, "Optimized for 2 color displays" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Optimizado para pantallas de 2 colores" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -24925,6 +29040,12 @@ "value" : "Optimiert für ATAK-Systemkommunikation, verringert die Anzahl der Routineübertragungen." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Optimizado para la comunicación del sistema ATAK, reduce las transmisiones de rutina." + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -24983,6 +29104,12 @@ }, "Optional fields to include when assembling position messages. the more fields are included, the larger the message will be - leading to longer airtime and a higher risk of packet loss" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Campos opcionales para incluir al ensamblar mensajes de posición. Cuantos más campos se incluyan, más grande será el mensaje, lo que llevará a un mayor tiempo de emisión y a un mayor riesgo de pérdida de paquetes." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -25017,6 +29144,12 @@ }, "Optional GPIO" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "GPIO opcional" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -25051,6 +29184,12 @@ "value" : "Optionen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opciones" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -25109,6 +29248,12 @@ }, "OS Log Entry Details" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Detalles de entrada de registro del sistema operativo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -25137,6 +29282,12 @@ }, "OTA Updates are not supported on this NRF Device." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Las actualizaciones OTA no son compatibles con este dispositivo NRF." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -25171,6 +29322,12 @@ }, "OTA Updates are not supported on your platform." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Las actualizaciones OTA no son compatibles con su plataforma." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -25205,6 +29362,12 @@ }, "Other data sources" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Otras fuentes de datos" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -25239,6 +29402,12 @@ "value" : "Ausgabe von Echtzeit-Fehlersuchprotokollen über die serielle Schnittstelle, Anzeige und Export von positionskorrigierten Geräteprotokollen über Bluetooth." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Genere registros de depuración en vivo a través de serie, vea y exporte registros de dispositivos redactados en posición a través de Bluetooth." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -25267,6 +29436,12 @@ }, "Output pin buzzer GPIO " : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zumbador de pin de salida GPIO" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -25295,6 +29470,12 @@ }, "Output pin GPIO" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pin de salida GPIO" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -25323,6 +29504,12 @@ }, "Output pin vibra GPIO" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pin de salida vibración GPIO" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -25351,6 +29538,12 @@ }, "Overlanding" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Por tierra" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -25385,6 +29578,12 @@ }, "Override automatic OLED screen detection." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anule la detección automática de pantalla OLED." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -25413,6 +29612,12 @@ }, "Override default screen layout." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anular el diseño de pantalla predeterminado." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -25423,6 +29628,12 @@ }, "Packet Count" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recuento de paquetes" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -25439,6 +29650,12 @@ "value" : "Pairing Modus" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modo de emparejamiento" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -25503,6 +29720,12 @@ "value" : "Passwort" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contraseña" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -25567,6 +29790,12 @@ "value" : "Pause" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pausa" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -25625,6 +29854,12 @@ }, "PAX Counter" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contador de pasajeros" + } + }, "he" : { "stringUnit" : { "state" : "translated", @@ -25677,6 +29912,12 @@ }, "PAX Counter Config" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración del contador PAX" + } + }, "he" : { "stringUnit" : { "state" : "translated", @@ -25723,6 +29964,12 @@ }, "PAX Counter config received: %@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración del contador PAX recibida: %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -25757,6 +30004,12 @@ }, "PAX Counter Log" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Registro de contador de PAX" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -25797,6 +30050,12 @@ "value" : "PAX Counter message received for: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mensaje del contador de PAX recibido de: %@" + } + }, "he" : { "stringUnit" : { "state" : "translated", @@ -25849,6 +30108,12 @@ }, "paxcounter.log" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "paxcounter.log" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -25877,6 +30142,12 @@ "value" : "Verbundenen Knoten auf Werkseinstellungen zurücksetzen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Realice un restablecimiento de fábrica en el nodo al que está conectado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -25905,6 +30176,12 @@ }, "Philippines 433MHz" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filipinas 433MHz" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -25933,6 +30210,12 @@ }, "Philippines 868MHz" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filipinas 868MHz" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -25961,6 +30244,12 @@ }, "Philippines 915MHz" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filipinas 915MHz" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -25995,6 +30284,12 @@ "value" : "Telefon GPS" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "GPS del teléfono" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -26059,6 +30354,12 @@ "value" : "Standorteinstellungen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ubicación del teléfono" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -26069,6 +30370,12 @@ }, "Pin %lld" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fijar %lld" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -26097,6 +30404,12 @@ }, "Pin A" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pin A" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -26125,6 +30438,12 @@ }, "Pin B" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pin B" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -26159,6 +30478,12 @@ "value" : "PKI-basierte Knotenadministration, benötigt Firmware Version 2.5+" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Administración de nodos basada en PKI, requiere versión de firmware 2.5+" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -26193,6 +30518,12 @@ }, "Please be advised that because the map report is not encrypted, your data may be stored and displayed permanently by third parties. Meshtastic does not assume responsibility for any such storage, display or disclosure of this data." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tenga en cuenta que debido a que el informe del mapa no está cifrado, terceros pueden almacenar y mostrar sus datos de forma permanente. Meshtastic no asume responsabilidad por dicho almacenamiento, exhibición o divulgación de estos datos." + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -26221,6 +30552,12 @@ "value" : "Bitte verbinde dich mit einem Funkgerät, um die Einstellungen zu ändern." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conéctese a una radio para configurar los ajustes." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -26261,6 +30598,12 @@ "value" : "Bitte lege eine Region fest" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Por favor establece una región" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -26289,6 +30632,12 @@ }, "Points of Interest" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Puntos de interés" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -26323,6 +30672,12 @@ "value" : "Kacke" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Caca" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -26381,6 +30736,12 @@ }, "Position" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Posición" + } + }, "he" : { "stringUnit" : { "state" : "translated", @@ -26433,6 +30794,12 @@ "value" : "Positionseinstellungen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de posición" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -26497,6 +30864,12 @@ "value" : "Positionskonfiguration empfangen: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de posición recibida: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -26549,6 +30922,12 @@ }, "Position Exchange Failed" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error en el intercambio de posición" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -26577,6 +30956,12 @@ }, "Position Exchange Requested" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intercambio de posición solicitado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -26605,6 +30990,12 @@ }, "Position Flags" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Banderas de posición" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -26633,6 +31024,12 @@ }, "Position Log" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Registro de posición" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -26667,6 +31064,12 @@ }, "Position Log %lld Points" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Registro de posición %lld puntos" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -26695,6 +31098,12 @@ }, "Position Packet" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paquete de posición" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -26729,6 +31138,12 @@ "value" : "Position gesendet" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Posición enviada" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -26757,6 +31172,12 @@ }, "Positions Enabled" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Posiciones Habilitadas" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -26791,6 +31212,12 @@ }, "Positions will be provided by your device GPS, if you select disabled or not present you can set a fixed position." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Las posiciones serán proporcionadas por el GPS de su dispositivo; si selecciona deshabilitado o no presente, puede establecer una posición fija." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -26831,6 +31258,12 @@ "value" : "Strom" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fuerza" + } + }, "he" : { "stringUnit" : { "state" : "translated", @@ -26889,6 +31322,12 @@ "value" : "Stromkonfiguration" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de energía" + } + }, "he" : { "stringUnit" : { "state" : "translated", @@ -26941,6 +31380,12 @@ }, "Power config received: %@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de energía recibida: %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -26975,6 +31420,12 @@ }, "Power Metrics" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Métricas de energía" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -27003,6 +31454,12 @@ }, "Power Metrics Log" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Registro de métricas de energía" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -27037,6 +31494,12 @@ }, "Power Off" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apagar" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -27066,6 +31529,12 @@ "Power Options" : { "extractionState" : "stale", "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opciones de energía" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -27100,6 +31569,12 @@ "value" : "Stromsparen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ahorro de energía" + } + }, "he" : { "stringUnit" : { "state" : "translated", @@ -27152,6 +31627,12 @@ }, "Power Screen" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pantalla de energía" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -27179,7 +31660,14 @@ } }, "Power Sensor Options" : { - + "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opciones de sensores de potencia" + } + } + } }, "Powered" : { "localizations" : { @@ -27189,6 +31677,12 @@ "value" : "Angeschaltet" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Motorizado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -27223,6 +31717,12 @@ "value" : "Genaue Position" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ubicación precisa" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -27257,6 +31757,12 @@ "value" : "Voreinstellungen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Preajustes" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -27291,6 +31797,12 @@ }, "Press Pin" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pin de prensa" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -27319,6 +31831,12 @@ }, "Pressure" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Presión" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -27353,6 +31871,12 @@ "value" : "Primär" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Primario" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -27417,6 +31941,12 @@ "value" : "Erster Admin-Schlüssel" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clave de administrador principal" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -27451,6 +31981,12 @@ }, "Primary GPIO" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "GPIO primario" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -27485,6 +32021,12 @@ "value" : "Privater Schlüssel" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clave privada" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -27525,6 +32067,12 @@ "value" : "Prozess" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Proceso" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -27589,6 +32137,12 @@ "value" : "Datei wird verarbeitet…" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Procesando archivo..." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -27629,6 +32183,12 @@ "value" : "Projektinformationen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Información del proyecto" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -27669,6 +32229,12 @@ "value" : "Protobufs" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Protobufs" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -27733,6 +32299,12 @@ "value" : "Teile anonyme Nutzungsstatistiken und Absturzberichte." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Proporcione estadísticas de uso anónimas e informes de fallos." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -27743,6 +32315,12 @@ }, "Provide Confirmation" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Proporcionar confirmación" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -27759,6 +32337,12 @@ "value" : "Öffentlicher Schlüssel" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clave pública" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -27793,6 +32377,12 @@ }, "Public Key Encryption" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cifrado de clave pública" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -27827,6 +32417,12 @@ }, "Public Key Mismatch" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La clave pública no coincide" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -27861,6 +32457,12 @@ }, "PWD" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "PCD" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -27895,6 +32497,12 @@ "value" : "Fragezeichen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pregunta" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -27953,6 +32561,12 @@ }, "Radiation" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Radiación" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -27987,6 +32601,12 @@ "value" : "Geräteeinstellungen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de radio" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -28051,6 +32671,12 @@ "value" : "RAK Drehimpulsgeber Modul" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Codificador rotatorio RAK" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -28115,6 +32741,12 @@ "value" : "Entfernungstest" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prueba de rango" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -28179,6 +32811,12 @@ "value" : "Entfernungstest Konfiguration" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de prueba de rango" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -28243,6 +32881,12 @@ "value" : "Range Test Modul konfiguration empfangen: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración del módulo de prueba de rango recibida: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -28307,6 +32951,12 @@ "value" : "Neustart" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reiniciar" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -28371,6 +33021,12 @@ "value" : "Knoten neustarten?" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Reiniciar el nodo?" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -28429,6 +33085,12 @@ }, "Rebroadcast any observed message, if it was on our private channel or from another mesh with the same lora params." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retransmitir cualquier mensaje observado, si fue en nuestro canal privado o desde otra malla con los mismos parámetros de lora." + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -28451,6 +33113,12 @@ }, "Rebroadcast Mode" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modo de retransmisión" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -28485,6 +33153,12 @@ }, "Receive data (rxd) GPIO pin" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recibir datos (rxd) pin GPIO" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -28519,6 +33193,12 @@ "value" : "Negative Empfangsbestätigung empfangen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recibí un reconocimiento negativo." + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -28584,6 +33264,12 @@ "value" : "Empfangsbestätigung" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Confirmación recibida" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -28652,6 +33338,12 @@ "value" : "Recipient Ack" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Confirmación del destinatario" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -28719,6 +33411,12 @@ "value" : "Route aufzeichnen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ruta de grabación" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -28747,6 +33445,12 @@ }, "Refresh device metadata" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actualizar metadatos del dispositivo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -28775,6 +33479,12 @@ }, "Regenerate Private Key" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Regenerar clave privada" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -28797,6 +33507,12 @@ "value" : "Region" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Región" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -28837,6 +33553,12 @@ "value" : "Regionale Einschaltdauergrenze erreicht" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se alcanzó el límite del ciclo de trabajo regional" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -28905,6 +33627,12 @@ }, "Release Notes" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notas de la versión" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -28933,6 +33661,12 @@ }, "Remote administration for: %@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Administración remota para: %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -28961,6 +33695,12 @@ }, "Remote Legacy Admin: %@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Administrador remoto heredado: %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -28989,6 +33729,12 @@ }, "Remote PKI Admin: %@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Administrador de PKI remoto: %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -29023,6 +33769,12 @@ "value" : "Entfernen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eliminar" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -29057,6 +33809,12 @@ "value" : "Von Favoriten entfernen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Quitar de favoritos" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -29085,6 +33843,12 @@ }, "Remove from ignored" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Quitar de ignorado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -29125,6 +33889,12 @@ "value" : "Repeater" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reloj de repetición" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -29159,6 +33929,12 @@ }, "Replace Channels" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reemplazar canales" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -29193,6 +33969,12 @@ "value" : "Antworten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Responder" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -29251,6 +34033,12 @@ }, "Request Legacy Admin: %@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Solicitar administrador heredado: %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -29279,6 +34067,12 @@ }, "Request PKI Admin: %@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Solicitar administrador de PKI: %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -29307,6 +34101,12 @@ }, "Requested Canned Messages Module Messages for node: %@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mensajes del módulo de mensajes predefinidos solicitados para el nodo: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -29365,6 +34165,12 @@ }, "Requires that there be an accelerometer on your device." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Requiere que haya un acelerómetro en su dispositivo." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -29399,6 +34205,12 @@ "value" : "App-Einstellungen zurücksetzen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restablecer la configuración de la aplicación" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -29439,6 +34251,12 @@ "value" : "Knotendatenbank zurücksetzen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restablecer NodeDB" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -29473,6 +34291,12 @@ "value" : "Neustarten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reanudar" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -29507,6 +34331,12 @@ "value" : "Verbundenen Knoten neustarten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reinicie en el nodo al que está conectado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -29535,6 +34365,12 @@ }, "Restore" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restaurar" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -29557,6 +34393,12 @@ "value" : "Fortsetzen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reanudar" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -29615,6 +34457,12 @@ }, "Retreiving nodes . ." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recuperando nodos. ." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -29625,6 +34473,12 @@ }, "Retreiving nodes %lld" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recuperando nodos %lld" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -29635,6 +34489,12 @@ }, "Retrieving nodes" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recuperando nodos" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -29645,6 +34505,12 @@ }, "Retrieving nodes %lld" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recuperando nodos %lld" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -29655,6 +34521,12 @@ }, "Retrying (attempt %lld)" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reintentando (intento %lld)" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -29671,6 +34543,12 @@ "value" : "App bewerten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Revisa la aplicación" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -29711,6 +34589,12 @@ "value" : "Rechts" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bien" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -29775,6 +34659,12 @@ "value" : "Klingelton" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tono de llamada" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -29839,6 +34729,12 @@ "value" : "Klingelton Konfiguration" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de tono de llamada" + } + }, "he" : { "stringUnit" : { "state" : "translated", @@ -29891,6 +34787,12 @@ }, "Ringtone Transfer Language" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Idioma de transferencia de tono de llamada" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -29931,6 +34833,12 @@ }, "Ringtone Transfer Language(RTTTL) Ringtone String used by supported buzzers in external notifications." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lenguaje de transferencia de tono de llamada (RTTTL) Cadena de tono utilizada por los timbres compatibles en notificaciones externas." + } + }, "he" : { "stringUnit" : { "state" : "translated", @@ -29989,6 +34897,12 @@ "value" : "Rolle" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Role" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -30029,6 +34943,12 @@ "value" : "Rolle: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Role: %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -30063,6 +34983,12 @@ "value" : "Rollen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Roles" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -30091,6 +35017,12 @@ }, "Root Topic" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tema raíz" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -30119,6 +35051,12 @@ }, "Rotary 1" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Giratorio 1" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -30153,6 +35091,12 @@ }, "Route Back: %@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ruta de regreso: %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -30181,6 +35125,12 @@ }, "Route Lines" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Líneas de ruta" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -30215,6 +35165,12 @@ "value" : "Routenliste" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lista de rutas" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -30231,6 +35187,12 @@ "value" : "Route aufzeichnen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Grabador de ruta" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -30271,6 +35233,12 @@ "value" : "Routenaufzeichnung pausiert" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Grabación de ruta en pausa" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -30305,6 +35273,12 @@ "value" : "Route: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ruta: %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -30339,6 +35313,12 @@ "value" : "Router" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enrutador" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -30379,6 +35359,12 @@ "value" : "Router mit Verzögerung" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enrutador tarde" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -30413,6 +35399,12 @@ "value" : "Routenliste" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rutas" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -30453,6 +35445,12 @@ "value" : "Routing empfangen für RequestID: %@ Ack Status: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enrutamiento recibido para RequestID: %@ Estado de confirmación: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -30511,6 +35509,12 @@ }, "RSSI %@ dBm" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "RSSI %@dBm" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -30539,6 +35543,12 @@ }, "RSSI %ddB" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "RSSI %ddB" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -30567,6 +35577,12 @@ }, "RSSI %llddB" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "RSSI %llddB" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -30601,6 +35617,12 @@ "value" : "RTTTL Klingeltonkonfiguration empfangen: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de tono de llamada RTTTL recibida: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -30659,6 +35681,12 @@ }, "Russia" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rusia" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -30681,6 +35709,12 @@ }, "RX Boosted Gain" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ganancia impulsada por RX" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -30709,6 +35743,12 @@ }, "Same as behavior as ALL but skips packet decoding and simply rebroadcasts them. Only available in Repeater role. Setting this on any other roles will result in ALL behavior." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Igual que el comportamiento de ALL, pero omite la decodificación de paquetes y simplemente los retransmite. Sólo disponible en rol de Repetidor. Establecer esto en cualquier otro rol dará como resultado TODOS los comportamientos." + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -30737,6 +35777,12 @@ "value" : "Satellit" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Satélite" + } + }, "he" : { "stringUnit" : { "state" : "translated", @@ -30783,6 +35829,12 @@ }, "Satellite Flyover" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sobrevuelo satelital" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -30847,6 +35899,12 @@ "value" : "Satelliten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "sábados" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -30881,6 +35939,12 @@ "value" : "Satelliten Schätzung %lld" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estimación de sats %lld" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -30915,6 +35979,12 @@ "value" : "Satelliten in Sicht: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sats a la vista: %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -30949,6 +36019,12 @@ "value" : "Speichern" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ahorrar" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -31007,6 +36083,12 @@ }, "Save Channel Settings" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Guardar configuración del canal" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -31041,6 +36123,12 @@ "value" : "Speichere Konfiguration für %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Guardar configuración para %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -31105,6 +36193,12 @@ "value" : "Benutzerkonfiguration nach %@ speichern?" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Guardar configuración de usuario en %@?" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -31139,6 +36233,12 @@ }, "Saves a CSV with the range test message details, currently only available on ESP32 devices with a web server." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Guarda un CSV con los detalles del mensaje de prueba de rango, actualmente solo disponible en dispositivos ESP32 con un servidor web." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -31173,6 +36273,12 @@ }, "Scan this QR code to add %@ to another device." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Escanee este código QR para agregar %@ a otro dispositivo." + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -31195,6 +36301,12 @@ }, "Screen on for" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pantalla encendida para" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -31229,6 +36341,12 @@ "value" : "Suchen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buscar" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -31257,6 +36375,12 @@ }, "Second" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Segundo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -31291,6 +36415,12 @@ "value" : "Sekundär" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Secundario" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -31355,6 +36485,12 @@ "value" : "Zweiter Admin-Schlüssel" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clave de administrador secundario" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -31395,6 +36531,12 @@ "value" : "Sicherheit" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seguridad" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -31435,6 +36577,12 @@ "value" : "Sicherheitskonfiguration" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de seguridad" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -31475,6 +36623,12 @@ "value" : "Sicherheitskonfigurationseinstellungen erfordern eine Firmware mit Version 2.5 oder höher" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los ajustes de configuración de seguridad requieren una versión de firmware 2.5+" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -31515,6 +36669,12 @@ "value" : "Auswählen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleccionar" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -31579,6 +36739,12 @@ "value" : "Kanal wählen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleccione un canal" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -31607,6 +36773,12 @@ }, "Select a conversation" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleccione una conversación" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -31635,6 +36807,12 @@ }, "Select a conversation type" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleccione un tipo de conversación" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -31668,10 +36846,23 @@ } }, "Select a Node" : { - + "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleccione un nodo" + } + } + } }, "Select a node from the drop down to manage connected or remote devices." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleccione un nodo del menú desplegable para administrar dispositivos conectados o remotos." + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -31694,6 +36885,12 @@ }, "Select a Trace Route" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleccione una ruta de seguimiento" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -31722,6 +36919,12 @@ }, "Select Channel" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleccionar canal" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -31756,6 +36959,12 @@ "value" : "Datei auswählen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleccionar archivo de mapa" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -31772,6 +36981,12 @@ "value" : "Als kritisch eingestufte Mitteilungen ignorieren den Stummschalter und die 'Nicht stören'-Einstellungen des Benachrichtigungszentrums." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los paquetes seleccionados enviados como críticos ignorarán el interruptor de silencio y la configuración de No molestar en el centro de notificaciones del sistema operativo." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -31788,6 +37003,12 @@ "value" : "Senden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviar" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -31822,6 +37043,12 @@ "value" : "Sende ${messageContent} an ${channelNumber}" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviar ${messageContent} a ${channelNumber}" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -31850,6 +37077,12 @@ }, "Send ${messageContent} to ${nodeNumber}" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviar ${messageContent} a ${nodeNumber}" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -31878,6 +37111,12 @@ }, "Send a Direct Message" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviar un mensaje directo" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -31894,6 +37133,12 @@ "value" : "Gruppennachricht senden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviar un mensaje grupal" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -31922,6 +37167,12 @@ }, "Send a heartbeat to advertise the server's presence." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envíe un latido para anunciar la presencia del servidor." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -31950,6 +37201,12 @@ }, "Send a message to a certain meshtastic channel" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviar un mensaje a un determinado canal meshtastic" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -31978,6 +37235,12 @@ }, "Send a message to a certain meshtastic node" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviar un mensaje a un determinado nodo meshtastic" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -31988,6 +37251,12 @@ }, "Send a position on the primary channel when the user button is triple clicked." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envíe una posición en el canal principal cuando se haga triple clic en el botón del usuario." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -32028,6 +37297,12 @@ "value" : "Herunterfahren an verbundenen Knoten senden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envía un apagado al nodo al que estás conectado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -32062,6 +37337,12 @@ "value" : "Wegpunkt senden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviar un punto de ruta" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -32090,6 +37371,12 @@ }, "Send ASCII bell with alert message. Useful for triggering external notification on bell." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviar campana ASCII con mensaje de alerta. Útil para activar notificaciones externas al tocar el timbre." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -32130,6 +37417,12 @@ "value" : "Sende Glocke" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviar campana" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -32170,6 +37463,12 @@ "value" : "Herzschlag senden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviar latido" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -32234,6 +37533,12 @@ "value" : "Mitteilungen senden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviar notificaciones" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -32244,6 +37549,12 @@ }, "Send Reboot OTA" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviar Reiniciar OTA" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -32278,6 +37589,12 @@ }, "Sender Interval" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intervalo del remitente" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -32318,6 +37635,12 @@ "value" : "Sensor" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensor" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -32352,6 +37675,12 @@ }, "Sensor options" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opciones de sensores" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -32381,6 +37710,12 @@ "Sensor Options" : { "extractionState" : "stale", "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opciones de sensores" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -32409,6 +37744,12 @@ }, "Sent a Channel for: %@ Channel Index %d" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envió un canal para: %@ Índice de canales %d" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -32473,6 +37814,12 @@ "value" : "LoRa.Config gesendet für: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envió un LoRa.Config para: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -32538,6 +37885,12 @@ "value" : "Position von Apple Gerät an Knoten gesendet: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envió un paquete de posición desde el GPS del dispositivo Apple al nodo: %@@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -32602,6 +37955,12 @@ "value" : "Sende Traceroute Anforderung zu Knoten: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envió una solicitud de ruta de seguimiento al nodo: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -32666,6 +38025,12 @@ "value" : "Wegpunkt gesendet von: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviado un paquete de waypoint desde: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -32730,6 +38095,12 @@ "value" : "Sende Nachricht %@ von %@ an %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mensaje enviado %@ de %@ a %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -32794,6 +38165,12 @@ "value" : "Sequenznummer" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Número de secuencia" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -32828,6 +38205,12 @@ "value" : "Sequenz: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Secuencia: %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -32856,6 +38239,12 @@ }, "Serial" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "De serie" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -32920,6 +38309,12 @@ "value" : "Serial Konfiguration" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración en serie" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -32984,6 +38379,12 @@ "value" : "Serielle Konsole" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Consola serie" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -33018,6 +38419,12 @@ "value" : "Serielle Konsole über die Stream-API." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Consola serial a través de Stream API." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -33052,6 +38459,12 @@ "value" : "Serial Modul Konfiguration empfangen: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración del módulo serie recibida: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -33110,6 +38523,12 @@ }, "Series" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Serie" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -33144,6 +38563,12 @@ "value" : "Server" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Servidor" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -33178,6 +38603,12 @@ "value" : "Serveradresse" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dirección del servidor" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -33206,6 +38637,12 @@ }, "Server Option" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opción de servidor" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -33234,6 +38671,12 @@ }, "Set" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Colocar" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -33268,6 +38711,12 @@ "value" : "Setze LoRa Region" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer región LoRa" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -33326,6 +38775,12 @@ }, "Set the GPIO pins for RXD and TXD." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configure los pines GPIO para RXD y TXD." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -33354,6 +38809,12 @@ }, "Set to current location" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer en la ubicación actual" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -33370,6 +38831,12 @@ }, "Sets the maximum number of hops, default is 3. Increasing hops also increases congestion and should be used carefully. O hop broadcast messages will not get ACKs." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establece el número máximo de saltos; el valor predeterminado es 3. El aumento de saltos también aumenta la congestión y debe usarse con cuidado. Los mensajes de difusión de O hop no recibirán ACK." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -33398,6 +38865,12 @@ }, "Sets the screen clock format to 12-hour." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establece el formato del reloj de la pantalla en 12 horas." + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -33420,6 +38893,12 @@ "value" : "Einstellungen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "ajustes" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -33436,6 +38915,12 @@ "value" : "Einstellungen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajustes" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -33500,6 +38985,12 @@ "value" : "Zweiundsiebzig Stunden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Setenta y dos horas" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -33558,6 +39049,12 @@ }, "Share Contact QR" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compartir Contacto QR" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -33586,6 +39083,12 @@ "value" : "Standort teilen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compartir ubicación" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -33602,6 +39105,12 @@ "value" : "Kanal QR Code teilen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compartir código QR" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -33666,6 +39175,12 @@ "value" : "QR Code & Link teilen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compartir código QR y enlace" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -33700,6 +39215,12 @@ "value" : "Teile deinen Standort in Echtzeit und koordiniere deine Gruppe mithilfe integrierter GPS-Funktionen." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comparta su ubicación en tiempo real y mantenga a su grupo coordinado con funciones de GPS integradas." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -33716,6 +39237,12 @@ "value" : "Gemeinsamer Schlüssel" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clave compartida" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -33750,6 +39277,12 @@ "value" : "Meshtastic Kanäle teilen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compartir canales Meshtastic" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -33814,6 +39347,12 @@ "value" : "Kurzname" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre corto" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -33848,6 +39387,12 @@ }, "Short Range - Fast" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Corto alcance - Rápido" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -33876,6 +39421,12 @@ }, "Short Range - Slow" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Corto alcance - Lento" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -33904,6 +39455,12 @@ }, "Short Range - Turbo" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Corto alcance - Turbo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -33932,6 +39489,12 @@ }, "Show a confirmation dialog before performing the factory reset" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar un cuadro de diálogo de confirmación antes de realizar el restablecimiento de fábrica" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -33948,6 +39511,12 @@ "value" : "Zeige Alarme" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar alertas" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -33982,6 +39551,12 @@ "value" : "Zeige Alarme" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar alertas" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -34016,6 +39591,12 @@ "value" : "Zeige Knoten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar nodos" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -34050,6 +39631,12 @@ "value" : "Zeige auf dem Gerätebildschirm" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar en la pantalla del dispositivo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -34084,6 +39671,12 @@ "value" : "Zeige auf der Netzwerkkarte." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar en el mapa de malla." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -34119,6 +39712,12 @@ "value" : "Zeige Wegpunkte" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar puntos de ruta" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -34147,6 +39746,12 @@ }, "Shows information for the connected Lora radio. You can swipe left to disconnect the radio and long press to start the live activity." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Muestra información de la radio Lora conectada. Puede deslizar hacia la izquierda para desconectar la radio y mantener presionada para iniciar la actividad en vivo." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -34163,6 +39768,12 @@ "value" : "Herunterfahren" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cerrar" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -34197,6 +39808,12 @@ "value" : "Knoten herunterfahren?" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Cerrar el nodo?" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -34231,6 +39848,12 @@ "value" : "Knoten herunterfahren?" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Apagar el nodo?" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -34265,6 +39888,12 @@ "value" : "Herunterfahren bei Stromunterbruch" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apagado por pérdida de energía" + } + }, "he" : { "stringUnit" : { "state" : "translated", @@ -34317,6 +39946,12 @@ }, "Signal %@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Señal %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -34351,6 +39986,12 @@ "value" : "Einfach" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Simple" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -34409,6 +40050,12 @@ }, "Singapore 923MHz" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Singapur 923MHz" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -34443,6 +40090,12 @@ "value" : "Sechs Stunden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seis horas" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -34507,6 +40160,12 @@ "value" : "Skifahren" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Esquiar" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -34541,6 +40200,12 @@ }, "Smart Position" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Posición inteligente" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -34569,6 +40234,12 @@ }, "SNR" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "SNR" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -34597,6 +40268,12 @@ }, "SNR %@ dB" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "SNR %@dB" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -34625,6 +40302,12 @@ }, "SNR %@dB" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "SNR %@dB" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -34653,6 +40336,12 @@ }, "Soil Moisture" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Humedad del suelo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -34681,6 +40370,12 @@ }, "Soil Temp" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temperatura del suelo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -34710,6 +40405,12 @@ "Specifies how long the monitored GPIO should output." : { "extractionState" : "stale", "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Especifica cuánto tiempo debe emitir el GPIO monitoreado." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -34744,6 +40445,12 @@ "value" : "Geschwindigkeit" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Velocidad" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -34778,6 +40485,12 @@ "value" : "Geschwindigkeit %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Velocidad %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -34812,6 +40525,12 @@ "value" : "Geschwindigkeit: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Velocidad: %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -34846,6 +40565,12 @@ "value" : "App-Entwicklung unterstützen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desarrollo de aplicaciones para patrocinadores" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -34862,6 +40587,12 @@ }, "Spread Factor" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Factor de dispersión" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -34896,6 +40627,12 @@ "value" : "SSID" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "SSID" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -34954,6 +40691,12 @@ }, "Standard" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estándar" + } + }, "he" : { "stringUnit" : { "state" : "translated", @@ -35000,6 +40743,12 @@ }, "Standard Muted" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estándar silenciado" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -35064,6 +40813,12 @@ "value" : "Start" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comenzar" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -35122,6 +40877,12 @@ }, "State Broadcast Interval" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intervalo de transmisión estatal" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -35156,6 +40917,12 @@ "value" : "Überall in Verbindung bleiben" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manténgase conectado en cualquier lugar" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -35166,6 +40933,12 @@ }, "Store & Forward" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Almacenar y reenviar" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -35200,6 +40973,12 @@ }, "Store & Forward Config" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Almacenar y reenviar configuración" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -35234,6 +41013,12 @@ }, "Store & Forward module config received: %@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración del módulo Store & Forward recibida: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -35292,6 +41077,12 @@ }, "Store and forward servers require an ESP32 device with PSRAM or Linux Native." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los servidores de almacenamiento y reenvío requieren un dispositivo ESP32 con PSRAM o Linux Native." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -35320,6 +41111,12 @@ }, "Subscribed" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "suscrito" + } + }, "he" : { "stringUnit" : { "state" : "translated", @@ -35354,6 +41151,12 @@ }, "Subsystem" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "subsistema" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -35394,6 +41197,12 @@ "value" : "Successfully uploaded '%1$@' with %2$lld overlays" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "'%@' subido correctamente con %lld superposiciones" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -35410,6 +41219,12 @@ "value" : "Unterstützt" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apoyado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -35438,6 +41253,12 @@ }, "Supported I2C Connected sensors will be detected automatically, sensors are BMP280, BME280, BME680, MCP9808, INA219, INA260, LPS22 and SHTC3." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los sensores conectados I2C compatibles se detectarán automáticamente, los sensores son BMP280, BME280, BME680, MCP9808, INA219, INA260, LPS22 y SHTC3." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -35472,6 +41293,12 @@ }, "Table" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mesa" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -35500,6 +41327,12 @@ }, "Taiwan" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Taiwán" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -35528,6 +41361,12 @@ "value" : "TAK" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "NO" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -35568,6 +41407,12 @@ "value" : "TAK Tracker" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "SÍ rastreador" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -35602,6 +41447,12 @@ }, "Takes a Meshtastic channel URL and saves the channel settings." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toma la URL de un canal Meshtastic y guarda la configuración del canal." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -35630,6 +41481,12 @@ }, "Takes a Meshtastic contact URL and saves it to the nodes database" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toma una URL de contacto Meshtastic y la guarda en la base de datos de nodos" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -35658,6 +41515,12 @@ "value" : "Tapback Antwort" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tapback" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -35715,6 +41578,14 @@ } }, "TCP" : { + "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "tcp" + } + } + }, "shouldTranslate" : false }, "Telemetry" : { @@ -35725,6 +41596,12 @@ "value" : "Telemetrie (Sensoren)" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Telemetria" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -35789,6 +41666,12 @@ "value" : "Telemetrie Einstellungen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de telemetría" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -35853,6 +41736,12 @@ "value" : "Telemetrie Modul Konfiguration empfangen: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración del módulo de telemetría recibida: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -35917,6 +41806,12 @@ "value" : "Temp" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temperatura" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -35951,6 +41846,12 @@ "value" : "Temperatur" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temperatura" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -35985,6 +41886,12 @@ "value" : "Zehn Minuten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "diez minutos" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -36049,6 +41956,12 @@ "value" : "Zehn Sekunden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Diez segundos" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -36113,6 +42026,12 @@ "value" : "Dritter Admin-Schlüssel" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clave de administrador terciario" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -36153,6 +42072,12 @@ "value" : "Textnachricht" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mensaje de texto" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -36211,6 +42136,12 @@ }, "TFT Full Color Displays" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pantallas TFT a todo color" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -36239,6 +42170,12 @@ }, "Thailand" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tailandia" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -36267,6 +42204,12 @@ }, "The amount of time to wait before we consider your packet as done." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La cantidad de tiempo que debemos esperar antes de que consideremos que su paquete está listo." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -36295,6 +42238,12 @@ }, "The compass heading on the screen outside of the circle will always point north." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El rumbo de la brújula en la pantalla fuera del círculo siempre apuntará al norte." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -36329,6 +42278,12 @@ "value" : "Der Taupunkt ist gerade %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El punto de rocío es %@ en este momento." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -36357,6 +42312,12 @@ }, "The fastest that position updates will be sent if the minimum distance has been satisfied" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lo más rápido que se enviarán las actualizaciones de posición si se ha cumplido la distancia mínima" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -36391,6 +42352,12 @@ "value" : "Die letzten 4 Zeichen der MAC-Adresse des Geräts werden an den Kurznamen angehängt, um den BLE-Namen des Geräts festzulegen. Der Kurzname kann bis zu 4 Byte lang sein." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los últimos 4 de la dirección MAC del dispositivo se agregarán al nombre corto para configurar el nombre BLE del dispositivo. El nombre corto puede tener hasta 4 bytes de longitud." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -36425,6 +42392,12 @@ }, "The maximum interval that can elapse without a node broadcasting a position" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El intervalo máximo que puede transcurrir sin que un nodo transmita una posición." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -36453,6 +42426,12 @@ }, "The Meshtastic Apple apps support firmware version %@ and above." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Las aplicaciones Meshtastic de Apple admiten la versión de firmware %@ y superior." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -36487,6 +42466,12 @@ }, "The minimum distance change in meters to be considered for a smart position broadcast." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El cambio mínimo de distancia en metros a considerar para una transmisión de posición inteligente." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -36527,6 +42512,12 @@ "value" : "Das Paket ist zu groß" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El paquete es demasiado grande." + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -36591,6 +42582,12 @@ "value" : "Der erste öffentliche Schlüssel, der berechtigt ist, Admin-Nachrichten an diesen Knoten zu senden." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La clave pública principal autorizada para enviar mensajes de administrador a este nodo." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -36631,6 +42628,12 @@ "value" : "Die Region, in der du deine Funkgeräte verwenden wirst." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La región donde utilizará sus radios." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -36665,6 +42668,12 @@ }, "The root topic to use for MQTT." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El tema raíz que se utilizará para MQTT." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -36699,6 +42708,12 @@ }, "The Router roles are only for high vantage locations like mountaintops and towers with few nearby nodes, not for use in urban areas. Improper use will hurt your local mesh." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Las funciones de enrutador son solo para ubicaciones estratégicas, como cimas de montañas y torres con pocos nodos cercanos, no para uso en áreas urbanas. El uso inadecuado dañará su malla local." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -36715,6 +42730,12 @@ "value" : "Der zweite öffentliche Schlüssel, der berechtigt ist, Admin-Nachrichten an diesen Knoten zu senden." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La clave pública secundaria autorizada para enviar mensajes de administrador a este nodo." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -36755,6 +42776,12 @@ "value" : "Status der LED (an/aus)" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El estado del LED (encendido/apagado)" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -36789,6 +42816,12 @@ "value" : "Der dritte öffentliche Schlüssel, der berechtigt ist, Admin-Nachrichten an diesen Knoten zu senden." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La clave pública terciaria autorizada para enviar mensajes de administrador a este nodo." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -36823,6 +42856,12 @@ }, "The URL for the channel settings" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La URL para la configuración del canal." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -36851,6 +42890,12 @@ }, "The URL for the node to add" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La URL del nodo a agregar." + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -36873,6 +42918,12 @@ }, "There has been no response to a request for device metadata via PKC admin for this node." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No ha habido respuesta a una solicitud de metadatos del dispositivo a través del administrador de PKC para este nodo." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -36883,6 +42934,12 @@ }, "There is an issue with this contact's public key." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hay un problema con la clave pública de este contacto." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -36898,6 +42955,12 @@ "These settings will %@ channels. The current LoRa Config will be replaced, if there are substantial changes to the LoRa config the device will reboot" : { "extractionState" : "stale", "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estas configuraciones %@ canales. La configuración LoRa actual será reemplazada; si hay cambios sustanciales en la configuración LoRa, el dispositivo se reiniciará" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -36932,6 +42995,12 @@ "value" : "Dreißig Minuten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "treinta minutos" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -36996,6 +43065,12 @@ "value" : "Dreißig Sekunden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "treinta segundos" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -37060,6 +43135,12 @@ "value" : "Sechsunddreissig Stunden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "treinta y seis horas" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -37118,6 +43199,12 @@ }, "This conversation will be deleted." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Esta conversación será eliminada." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -37146,6 +43233,12 @@ }, "This could take a while, response will appear in the trace route log for the node it was sent to." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Esto podría tardar un poco; la respuesta aparecerá en el registro de ruta de seguimiento del nodo al que se envió." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -37174,6 +43267,12 @@ }, "This device will send out range test messages on the selected interval." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Este dispositivo enviará mensajes de prueba de alcance en el intervalo seleccionado." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -37214,6 +43313,12 @@ "value" : "Diese Nachricht wurde höchstwahrscheinlich nicht übermittelt." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Es probable que este mensaje no se haya entregado." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -37242,6 +43347,12 @@ }, "This node does not support any configurable modules." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Este nodo no admite ningún módulo configurable." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -37270,6 +43381,12 @@ }, "This will disable fixed position and remove the currently set position." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Esto desactivará la posición fija y eliminará la posición establecida actualmente." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -37298,6 +43415,12 @@ }, "This will send a current position from your phone and enable fixed position." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Esto enviará una posición actual desde su teléfono y habilitará la posición fija." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -37338,6 +43461,12 @@ "value" : "Drei Stunden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "tres horas" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -37402,6 +43531,12 @@ "value" : "Drei Sekunden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "tres segundos" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -37466,6 +43601,12 @@ "value" : "Daumen runter" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pulgar hacia abajo" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -37530,6 +43671,12 @@ "value" : "Daumen hoch" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pulgares hacia arriba" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -37594,6 +43741,12 @@ "value" : "Zeit" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tiempo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -37628,6 +43781,12 @@ "value" : "Zeitstempel" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Marca de tiempo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -37662,6 +43821,12 @@ "value" : "Zeitzone" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Huso horario" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -37696,6 +43861,12 @@ "value" : "Zeitzone für Daten auf dem Gerätebildschirm und Log." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zona horaria para fechas en la pantalla del dispositivo y registro." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -37730,6 +43901,12 @@ "value" : "Zeitlimit erreicht" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se acabó el tiempo" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -37794,6 +43971,12 @@ "value" : "Zeitstempel" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Marca de tiempo" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -37852,6 +44035,12 @@ }, "Timing and Overrides" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temporización y anulaciones" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -37862,6 +44051,12 @@ }, "TLS Enabled" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "TLS habilitado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -37896,6 +44091,12 @@ }, "To comply with privacy laws like CCPA and GDPR, we avoid sharing exact location data. Instead, we use anonymized or approximate (imprecise) location information to protect your privacy." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Para cumplir con las leyes de privacidad como CCPA y GDPR, evitamos compartir datos de ubicación exacta. En su lugar, utilizamos información de ubicación anónima o aproximada (imprecisa) para proteger su privacidad." + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -37918,6 +44119,12 @@ }, "To Radio (TX): %lld" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "A radio (TX): %lld" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -37928,6 +44135,12 @@ }, "Topic: %@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tema: %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -37962,6 +44175,12 @@ "value" : "Total" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -37990,6 +44209,12 @@ }, "Total PAX" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "PAX TOTALES" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -38024,6 +44249,12 @@ }, "Trace Route" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ruta de seguimiento" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -38052,6 +44283,12 @@ }, "Trace Route (in %@s)" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ruta de seguimiento (en %@s)" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -38074,6 +44311,12 @@ }, "Trace Route Log" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Registro de ruta de seguimiento" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -38114,6 +44357,12 @@ "value" : "Traceroute Ergebnis: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Solicitud de ruta de seguimiento devuelta: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -38172,6 +44421,12 @@ }, "Trace Route Sent" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ruta de seguimiento enviada" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -38200,6 +44455,12 @@ }, "Trace route sent to %@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ruta de seguimiento enviada a %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -38228,6 +44489,12 @@ }, "Trace route to %@ was not sent." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La ruta de seguimiento a %@ no se envió." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -38256,6 +44523,12 @@ }, "Trace Route was rate limited. You can send a trace route a maximum of once every thirty seconds." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trace Route tenía una tarifa limitada. Puede enviar una ruta de rastreo como máximo una vez cada treinta segundos." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -38290,6 +44563,12 @@ "value" : "Standorte verfolgen und teilen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seguimiento y compartir ubicaciones" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -38300,6 +44579,12 @@ }, "Tracker" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rastreador" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -38334,6 +44619,12 @@ "value" : "Verkehr" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tráfico" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -38362,6 +44653,12 @@ }, "Transmit data (txd) GPIO pin" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Transmitir datos (txd) pin GPIO" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -38390,6 +44687,12 @@ }, "Transmit Enabled" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Transmisión habilitada" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -38424,6 +44727,12 @@ }, "Treat double tap on supported accelerometers as a user button press." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Considere el doble toque en los acelerómetros compatibles como si el usuario presionara un botón." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -38458,6 +44767,12 @@ }, "TriggerType" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tipo de disparador" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -38486,6 +44801,12 @@ }, "Triple Click Ad Hoc Ping" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ping ad hoc de triple clic" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -38520,6 +44841,12 @@ "value" : "Erneut versuchen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intentar otra vez" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -38554,6 +44881,12 @@ "value" : "Zwölf Stunden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Doce horas" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -38618,6 +44951,12 @@ "value" : "Vierundzwanzig Stunden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "veinticuatro horas" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -38682,6 +45021,12 @@ "value" : "Zwei Stunden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "dos horas" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -38746,6 +45091,12 @@ "value" : "Zwei Minutes" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "dos minutos" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -38810,6 +45161,12 @@ "value" : "Zwei Sekunden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "dos segundos" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -38868,6 +45225,12 @@ }, "UDP Broadcast" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Transmisión UDP" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -38896,6 +45259,12 @@ }, "Ukraine 433MHz" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ucrania 433MHz" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -38924,6 +45293,12 @@ }, "Ukraine 868MHz" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ucrania 868MHz" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -38952,6 +45327,12 @@ }, "Un-Favorite" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No favorito" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -38980,6 +45361,12 @@ }, "Unhealthy" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Malsano" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -39008,6 +45395,12 @@ }, "Unhealthy for Sensitive Groups" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No saludable para grupos sensibles" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -39036,6 +45429,12 @@ }, "United States" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estados Unidos" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -39064,6 +45463,12 @@ }, "Units displayed on the device screen" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unidades mostradas en la pantalla del dispositivo." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -39092,6 +45497,12 @@ }, "unknown" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "desconocido" + } + }, "it" : { "stringUnit" : { "state" : "needs_review", @@ -39120,6 +45531,12 @@ }, "Unknown" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desconocido" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -39184,6 +45601,12 @@ "value" : "Unbekanntes alter" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edad desconocida" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -39248,6 +45671,12 @@ "value" : "Nicht benachrichtigbar" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "inmensable" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -39264,6 +45693,12 @@ }, "Unmonitored" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No monitoreado" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -39286,6 +45721,12 @@ "value" : "Unset" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desarmado" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -39344,6 +45785,12 @@ }, "Unsupported" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No compatible" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -39378,6 +45825,12 @@ "value" : "Hoch" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Arriba" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -39436,6 +45889,12 @@ }, "Up Down 1" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "arriba abajo 1" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -39468,8 +45927,24 @@ } } }, + "UPDATE IN" : { + "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "ACTUALIZAR EN" + } + } + } + }, "Update Interval" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intervalo de actualización" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -39504,6 +45979,12 @@ "value" : "Firmware aktualisieren" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actualice su firmware" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -39562,6 +46043,12 @@ }, "Updated Node Stats Data." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datos de estadísticas de nodos actualizados." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -39596,6 +46083,12 @@ "value" : "Aktualisiert: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actualizado: %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -39624,6 +46117,12 @@ }, "Uplink Enabled" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enlace ascendente habilitado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -39664,6 +46163,12 @@ "value" : "Hochladen fehlgeschlagen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error de carga" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -39704,6 +46209,12 @@ "value" : "Lade GeoJSON-Dateien hoch, um eigene Karten-Overlays anzuzeigen. Die Dateien werden lokal gespeichert und dürfen bis zu 10 MB groß sein." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cargue archivos GeoJSON para mostrar superposiciones de mapas personalizados. Los archivos se almacenan localmente y pueden tener hasta 10 MB." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -39739,6 +46250,12 @@ "Upload Map Data" : { "comment" : "Title for map data upload screen", "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cargar datos del mapa" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -39780,6 +46297,12 @@ "value" : "Lade Kartendaten hoch, um Overlays zu aktivieren" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cargar datos de mapas para habilitar superposiciones" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -39820,6 +46343,12 @@ "value" : "Kartendaten hochladen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cargar superposiciones de mapas" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -39836,6 +46365,12 @@ "value" : "Hochladen erfolgreich" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Subir con éxito" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -39876,6 +46411,12 @@ "value" : "Hochgeladene Kartendaten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Superposiciones de mapas cargados" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -39886,6 +46427,12 @@ }, "Uptime" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "tiempo de actividad" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -39932,6 +46479,12 @@ "value" : "Telemetriedaten erfassen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datos de uso y fallos" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -39942,6 +46495,12 @@ }, "Use a PWM output (like the RAK Buzzer) for tunes instead of an on/off output. This will ignore the output, output duration and active settings and use the device config buzzer GPIO option instead." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utilice una salida PWM (como el RAK Buzzer) para melodías en lugar de una salida de encendido/apagado. Esto ignorará la salida, la duración de la salida y la configuración activa y en su lugar utilizará la opción GPIO del zumbador de configuración del dispositivo." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -39976,6 +46535,12 @@ }, "Use I2S As Buzzer" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utilice I2S como zumbador" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -40010,6 +46575,12 @@ "value" : "Standort verwenden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Usar mi ubicación" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -40032,6 +46603,12 @@ "value" : "Voreinstellung verwenden" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Usar preajuste" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -40066,6 +46643,12 @@ }, "Use PWM Buzzer" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Usar zumbador PWM" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -40106,6 +46689,12 @@ "value" : "Verwende das GPS deines Handys anstelle des GPS deines Funkgeräts." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utilice el GPS de su teléfono para enviar ubicaciones a su nodo en lugar de utilizar un GPS de hardware en su nodo." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -40116,6 +46705,12 @@ }, "Used to create a shared key with a remote device." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se utiliza para crear una clave compartida con un dispositivo remoto." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -40150,6 +46745,12 @@ "value" : "Wird verwendet, um nicht überwachte oder Infrastrukturknoten zu identifizieren, damit Nachrichten nicht an Knoten gesendet werden, die niemals antworten werden." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se utiliza para identificar nodos de infraestructura o no supervisados, de modo que la mensajería no esté disponible para nodos que nunca responderán." + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -40172,6 +46773,12 @@ "value" : "Benutzer" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Usuario" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -40236,6 +46843,12 @@ "value" : "Benutzerkonfiguration" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de usuario" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -40276,6 +46889,12 @@ "value" : "Benutzerdaten" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Detalles del usuario" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -40310,6 +46929,12 @@ }, "User Id" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Identificación de usuario" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -40354,6 +46979,12 @@ "value" : "Daten verfügbar" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Usuario subido" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -40394,6 +47025,12 @@ "value" : "Benutzername" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre de usuario" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -40452,6 +47089,12 @@ }, "Uses pullup resistor" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utiliza resistencia pullup" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -40480,6 +47123,12 @@ }, "Utilizes the network connection on your phone to connect to MQTT." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utiliza la conexión de red de su teléfono para conectarse a MQTT." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -40520,6 +47169,12 @@ "value" : "Fahrzeugsteuerkurs" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rumbo del vehículo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -40554,6 +47209,12 @@ "value" : "Fahrzeuggeschwindigkeit" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Velocidad del vehículo" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -40582,6 +47243,12 @@ }, "Verify who you are messaging with by comparing public keys in person or over the phone. The most recent public key for this node does not match the previously recorded key. You can delete the node and let it exchange keys again if the key change was due to a factory reset or other intentional action but this also may indicate a more serious security problem." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verifique con quién está enviando mensajes comparando claves públicas en persona o por teléfono. La clave pública más reciente para este nodo no coincide con la clave registrada anteriormente. Puede eliminar el nodo y dejar que intercambie claves nuevamente si el cambio de clave se debió a un restablecimiento de fábrica u otra acción intencional, pero esto también puede indicar un problema de seguridad más grave." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -40598,6 +47265,12 @@ "value" : "Version %1$@ includes substantial network optimizations and extensive changes to devices and client apps. Only nodes version %2$@ and above are supported." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La versión %@ incluye optimizaciones sustanciales de la red y cambios extensos en los dispositivos y aplicaciones cliente. Solo se admiten nodos de versión %@ y superiores." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -40638,6 +47311,12 @@ "value" : "Version: %1$@ (%2$@)" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versión: %@ (%@)" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -40666,6 +47345,12 @@ "value" : "Version: %1$@ (%2$@) " } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versión: %1$@ (%2$@)" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -40700,6 +47385,12 @@ }, "Very Unhealthy" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "muy poco saludable" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -40734,6 +47425,12 @@ "value" : "Via Lora" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vía Lora" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -40768,6 +47465,12 @@ "value" : "Via Mqtt" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vía Mqtt" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -40808,6 +47511,12 @@ "value" : "Voltage" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voltaje" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -40866,6 +47575,12 @@ }, "Volts %@" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voltios %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -40900,6 +47615,12 @@ "value" : "Warte..." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Espera" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -40958,6 +47679,12 @@ }, "Waiting to be acknowledged. . ." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Esperando ser reconocido. . ." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -40986,6 +47713,12 @@ }, "Wake Screen on tap or motion" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activar pantalla con un toque o movimiento" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -41020,6 +47753,12 @@ "value" : "Gehen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Caminando" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -41060,6 +47799,12 @@ "value" : "Welle" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ola" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -41094,6 +47839,12 @@ }, "Waypoint Failed to Send" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El punto de referencia no se pudo enviar" + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -41116,6 +47867,12 @@ "value" : "Wegpunktoptionen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opciones de punto de referencia" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -41150,6 +47907,12 @@ "value" : "Wegpunkt von Knoten empfangen: %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paquete de waypoint recibido del nodo: %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -41207,8 +47970,30 @@ } }, "Waypoints" : { - + "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Puntos de referencia" + } + } + } }, +<<<<<<< Updated upstream +======= + "We anonymously collect usage and crash data to improve the app. This helps us understand how the app is being used and where we can make improvements. The data we collect is non-personally identifiable and cannot be linked to you as an individual. You can opt out of this under app settings." : { + "comment" : "A description of how the app collects and uses user data. Includes a link to the app settings.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recopilamos de forma anónima datos de uso y fallos para mejorar la aplicación. Esto nos ayuda a comprender cómo se utiliza la aplicación y dónde podemos realizar mejoras. Los datos que recopilamos no son identificables personalmente y no pueden vincularse a usted como individuo. Puede optar por no participar en la configuración de la aplicación." + } + } + } + }, +>>>>>>> Stashed changes "Weather Conditions" : { "localizations" : { "de" : { @@ -41217,6 +48002,12 @@ "value" : "Wetterverhältnisse" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Condiciones climáticas" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -41251,6 +48042,12 @@ }, "Web Flasher" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intermitente web" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -41279,6 +48076,12 @@ }, "Website" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sitio web" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -41313,6 +48116,12 @@ }, "Weight" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Peso" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -41347,6 +48156,12 @@ "value" : "Willkommen bei" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bienvenido a" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -41363,6 +48178,12 @@ "value" : "Was bedeutet das Schloß?" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Qué significa la cerradura?" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -41403,6 +48224,12 @@ "value" : "Was ist Meshtastic?" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Qué es Meshtastic?" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -41437,6 +48264,12 @@ }, "What licensed operator mode does:\n* Sets the node name to your call sign \n* Broadcasts node info every 10 minutes \n* Overrides frequency, dutycycle and tx power \n* Disables encryption" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Qué hace el modo de operador con licencia:\n* Establece el nombre del nodo según su indicativo de llamada \n* Transmite información del nodo cada 10 minutos \n* Anula la frecuencia, el ciclo de trabajo y la potencia de transmisión. \n* Desactiva el cifrado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -41471,6 +48304,12 @@ }, "When enabled the PAX Counter module counts the number of people passing by using WiFi and Bluetooth. Both WiFI and Bluetooth must be disabled for PAX counter to work." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cuando está habilitado, el módulo Contador de PAX cuenta el número de personas que pasan mediante WiFi y Bluetooth. Tanto WiFI como Bluetooth deben estar desactivados para que funcione el contador de PAX." + } + }, "he" : { "stringUnit" : { "state" : "translated", @@ -41517,6 +48356,12 @@ }, "When using in GPIO mode, keep the output on for this long. " : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cuando lo use en modo GPIO, mantenga la salida encendida durante este tiempo." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -41551,6 +48396,12 @@ }, "Whether or not use INPUT_PULLUP mode for GPIO pin. Only applicable if the board uses pull-up resistors on the pin" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Si se utiliza o no el modo INPUT_PULLUP para el pin GPIO. Solo aplicable si la placa usa resistencias pull-up en el pin" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -41579,6 +48430,12 @@ }, "WiFi" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wi-Fi" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -41619,6 +48476,12 @@ "value" : "WiFi Optionen" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opciones WiFi" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -41647,6 +48510,12 @@ }, "Will sleep everything as much as possible, for the tracker and sensor role this will also include the lora radio. Don't use this setting if you want to use your device with the phone apps or are using a device without a user button." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dormirá todo lo más posible, para la función de rastreador y sensor esto también incluirá la radio lora. No use esta configuración si desea usar su dispositivo con las aplicaciones del teléfono o si está usando un dispositivo sin un botón de usuario." + } + }, "he" : { "stringUnit" : { "state" : "translated", @@ -41699,6 +48568,12 @@ }, "Wind" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Viento" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -41733,6 +48608,12 @@ "value" : "Windrichtung" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dirección del viento" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -41767,6 +48648,12 @@ "value" : "Windgeschwindigkeit" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Velocidad del viento" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -41801,6 +48688,12 @@ "value" : "Innerhalb %@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dentro %@" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -41835,6 +48728,12 @@ }, "x" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "incógnita" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -41869,6 +48768,12 @@ "value" : "X: %1$@, Y: %2$d" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "X: %@, Y: %d" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -41910,6 +48815,12 @@ "value" : "X: %1$@, Y: %2$f" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "X: %@, Y: %f" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -41951,6 +48862,12 @@ "value" : "X: %1$@, Y: %2$lld" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "X: %@, Y: %lld" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -41986,6 +48903,12 @@ }, "y" : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "y" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -42030,6 +48953,12 @@ "value" : "Gestern" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ayer" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -42058,6 +48987,12 @@ }, "You can also update your Meshtastic device over bluetooth using the Nordic DFU app." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "También puede actualizar su dispositivo Meshtastic a través de bluetooth utilizando la aplicación Nordic DFU." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -42098,6 +49033,12 @@ "value" : "Du kannst Kanalnachrichten (Gruppenchats) und Direktnachrichten senden und empfangen. Bei jeder Nachricht kannst du lange drücken, um verfügbare Aktionen wie Kopieren, Antworten, Tapback und Löschen sowie Zustelldetails anzuzeigen." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Puedes enviar y recibir canales (chats grupales) y mensajes directos. Desde cualquier mensaje, puede mantener presionado para ver las acciones disponibles como copiar, responder, retroceder y eliminar, así como los detalles de entrega." + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -42156,6 +49097,12 @@ }, "Your current location will be set as the fixed position and broadcast over the mesh on the position interval." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Su ubicación actual se establecerá como posición fija y se transmitirá sobre la malla en el intervalo de posición." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -42196,6 +49143,12 @@ "value" : "Deine Firmware ist aktuell" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Su firmware está actualizado" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -42230,6 +49183,12 @@ }, "Your MQTT Server must support TLS." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Su servidor MQTT debe admitir TLS." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -42258,6 +49217,12 @@ }, "Your node will periodically send an unencrypted map report packet to the configured MQTT server, this includes id, short and long name, approximate location, hardware model, role, firmware version, LoRa region, modem preset and primary channel name." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Su nodo enviará periódicamente un paquete de informe de mapa sin cifrar al servidor MQTT configurado, esto incluye identificación, nombre corto y largo, ubicación aproximada, modelo de hardware, función, versión de firmware, región LoRa, configuración predeterminada del módem y nombre del canal principal." + } + }, "ja" : { "stringUnit" : { "state" : "translated", @@ -42280,6 +49245,12 @@ }, "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." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La frecuencia operativa de su nodo se calcula en función de la región, la configuración predeterminada del módem y este campo. Cuando es 0, la ranura se calcula automáticamente en función del nombre del canal principal." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -42308,6 +49279,12 @@ }, "Your position has been sent with a request for a response with their position. You will receive a notification when a position is returned." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Su posición ha sido enviada con una solicitud de respuesta con su posición. Recibirá una notificación cuando se devuelva una posición." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -42341,10 +49318,23 @@ } }, "Your public key is generated from your private key and sent to other nodes on the mesh so they can compute a shared secret key with you." : { - + "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Su clave pública se genera a partir de su clave privada y se envía a otros nodos de la malla para que puedan calcular una clave secreta compartida con usted." + } + } + } }, "Your region has a %lld%% duty cycle. MQTT is not advised when you are duty cycle restricted, the extra traffic will quickly overwhelm your LoRa mesh." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Su región tiene un ciclo de trabajo %lld%%. No se recomienda MQTT cuando tiene un ciclo de trabajo restringido, el tráfico adicional abrumará rápidamente su malla LoRa." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -42379,6 +49369,12 @@ }, "Your region has a %lld%% hourly duty cycle, your radio will stop sending packets when it reaches the hourly limit." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Su región tiene un ciclo de trabajo por hora %lld%%, su radio dejará de enviar paquetes cuando alcance el límite por hora." + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -42407,6 +49403,12 @@ }, "Your route file must have both Latitude and Longitude columns and headers." : { "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Su archivo de ruta debe tener columnas y encabezados de Latitud y Longitud." + } + }, "it" : { "stringUnit" : { "state" : "translated", From 575cb887b0d057cdf138d1df43ba1a016a08ea38 Mon Sep 17 00:00:00 2001 From: Mathew Kamkar <578302+matkam@users.noreply.github.com> Date: Sun, 4 Jan 2026 20:26:51 -0800 Subject: [PATCH 09/15] tapback with any emoji (#1538) --- Localizable.xcstrings | 3 + Meshtastic.xcodeproj/project.pbxproj | 4 + Meshtastic/Helpers/EmojiOnlyTextField.swift | 46 ++++++++ .../Messages/MessageContextMenuItems.swift | 27 +---- Meshtastic/Views/Messages/MessageText.swift | 36 +++++- .../Views/Messages/TapbackInputView.swift | 108 ++++++++++++++++++ 6 files changed, 198 insertions(+), 26 deletions(-) create mode 100644 Meshtastic/Views/Messages/TapbackInputView.swift diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 78c9454b..877f87f1 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -36916,6 +36916,9 @@ } } } + }, + "Select an emoji" : { + }, "Select Channel" : { "localizations" : { diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index cd52c496..a504bb13 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -115,6 +115,7 @@ D93068D52B812B700066FBC8 /* MessageDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93068D42B812B700066FBC8 /* MessageDestination.swift */; }; D93068D72B8146690066FBC8 /* MessageText.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93068D62B8146690066FBC8 /* MessageText.swift */; }; D93068D92B81509C0066FBC8 /* TapbackResponses.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93068D82B81509C0066FBC8 /* TapbackResponses.swift */; }; + D93068DA2B81509D0066FBC8 /* TapbackInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93068D92B81509D0066FBC8 /* TapbackInputView.swift */; }; D93068DB2B81C85E0066FBC8 /* PowerConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93068DA2B81C85E0066FBC8 /* PowerConfig.swift */; }; D93068DD2B81CA820066FBC8 /* ConfigHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93068DC2B81CA820066FBC8 /* ConfigHeader.swift */; }; D93069082B81DF040066FBC8 /* SaveConfigButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93069072B81DF040066FBC8 /* SaveConfigButton.swift */; }; @@ -427,6 +428,7 @@ D93068D42B812B700066FBC8 /* MessageDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageDestination.swift; sourceTree = ""; }; D93068D62B8146690066FBC8 /* MessageText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageText.swift; sourceTree = ""; }; D93068D82B81509C0066FBC8 /* TapbackResponses.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TapbackResponses.swift; sourceTree = ""; }; + D93068D92B81509D0066FBC8 /* TapbackInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TapbackInputView.swift; sourceTree = ""; }; D93068DA2B81C85E0066FBC8 /* PowerConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PowerConfig.swift; sourceTree = ""; }; D93068DC2B81CA820066FBC8 /* ConfigHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigHeader.swift; sourceTree = ""; }; D93069062B81D8900066FBC8 /* MeshtasticDataModelV 27.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 27.xcdatamodel"; sourceTree = ""; }; @@ -1250,6 +1252,7 @@ D93068D62B8146690066FBC8 /* MessageText.swift */, D93068D22B8129510066FBC8 /* MessageContextMenuItems.swift */, D93068D82B81509C0066FBC8 /* TapbackResponses.swift */, + D93068D92B81509D0066FBC8 /* TapbackInputView.swift */, ); path = Messages; sourceTree = ""; @@ -1809,6 +1812,7 @@ DD4975A52B147BA90026544E /* AmbientLightingConfig.swift in Sources */, 3D3417D22E2DC260006A988B /* MapDataManager.swift in Sources */, D93068D92B81509C0066FBC8 /* TapbackResponses.swift in Sources */, + D93068DA2B81509D0066FBC8 /* TapbackInputView.swift in Sources */, DDA9F5E82E77FAC100E70DEB /* AnimatedNodePin.swift in Sources */, DDF82CBD2D5BC69200DC25EC /* NavigateToButton.swift in Sources */, 8D3F8A3F2D44BB02009EAAA4 /* PowerMetrics.swift in Sources */, diff --git a/Meshtastic/Helpers/EmojiOnlyTextField.swift b/Meshtastic/Helpers/EmojiOnlyTextField.swift index 0982ab33..4928da73 100644 --- a/Meshtastic/Helpers/EmojiOnlyTextField.swift +++ b/Meshtastic/Helpers/EmojiOnlyTextField.swift @@ -7,6 +7,7 @@ import SwiftUI class SwiftUIEmojiTextField: UITextField { + var shouldBecomeFirstResponderOnAppear = false func setEmoji() { _ = self.textInputMode @@ -23,22 +24,39 @@ class SwiftUIEmojiTextField: UITextField { } return nil } + + override func didMoveToWindow() { + super.didMoveToWindow() + if shouldBecomeFirstResponderOnAppear && window != nil { + DispatchQueue.main.async { [weak self] in + self?.becomeFirstResponder() + } + } + } } struct EmojiOnlyTextField: UIViewRepresentable { @Binding var text: String var placeholder: String = "" + var onBecomeFirstResponder: (() -> Void)? + var onKeyboardTypeChanged: ((Bool) -> Void)? // true if emoji, false otherwise + var onKeyboardDismissed: (() -> Void)? // Called when keyboard is dismissed func makeUIView(context: Context) -> SwiftUIEmojiTextField { let emojiTextField = SwiftUIEmojiTextField() emojiTextField.placeholder = placeholder emojiTextField.text = text emojiTextField.delegate = context.coordinator + emojiTextField.shouldBecomeFirstResponderOnAppear = true + context.coordinator.textField = emojiTextField return emojiTextField } func updateUIView(_ uiView: SwiftUIEmojiTextField, context: Context) { uiView.text = text + context.coordinator.onBecomeFirstResponder = onBecomeFirstResponder + context.coordinator.onKeyboardTypeChanged = onKeyboardTypeChanged + context.coordinator.onKeyboardDismissed = onKeyboardDismissed } func makeCoordinator() -> Coordinator { @@ -47,13 +65,41 @@ struct EmojiOnlyTextField: UIViewRepresentable { class Coordinator: NSObject, UITextFieldDelegate { var parent: EmojiOnlyTextField + var textField: SwiftUIEmojiTextField? + var onBecomeFirstResponder: (() -> Void)? + var onKeyboardTypeChanged: ((Bool) -> Void)? + var onKeyboardDismissed: (() -> Void)? + var previousInputMode: String? + init(parent: EmojiOnlyTextField) { self.parent = parent } + + func textFieldDidBeginEditing(_ textField: UITextField) { + onBecomeFirstResponder?() + checkInputMode(textField) + } + + func textFieldDidEndEditing(_ textField: UITextField) { + // Keyboard was dismissed + onKeyboardDismissed?() + } + func textFieldDidChangeSelection(_ textField: UITextField) { DispatchQueue.main.async { [weak self] in self?.parent.text = textField.text ?? "" } + checkInputMode(textField) + } + + private func checkInputMode(_ textField: UITextField) { + if let inputMode = textField.textInputMode { + let isEmoji = inputMode.primaryLanguage == "emoji" + if previousInputMode != inputMode.primaryLanguage { + previousInputMode = inputMode.primaryLanguage + onKeyboardTypeChanged?(!isEmoji) // true if NOT emoji (should dismiss) + } + } } } } diff --git a/Meshtastic/Views/Messages/MessageContextMenuItems.swift b/Meshtastic/Views/Messages/MessageContextMenuItems.swift index 63104320..14d5b3f7 100644 --- a/Meshtastic/Views/Messages/MessageContextMenuItems.swift +++ b/Meshtastic/Views/Messages/MessageContextMenuItems.swift @@ -10,6 +10,7 @@ struct MessageContextMenuItems: View { let tapBackDestination: MessageDestination let isCurrentUser: Bool @Binding var isShowingDeleteConfirmation: Bool + @Binding var isShowingTapbackInput: Bool let onReply: () -> Void @State var relayDisplay: String? = nil @@ -29,30 +30,8 @@ struct MessageContextMenuItems: View { } } - Menu("Tapback") { - ForEach(Tapbacks.allCases) { tb in - Button { - Task { - do { - try await accessoryManager.sendMessage( - message: tb.emojiString, - toUserNum: tapBackDestination.userNum, - channel: tapBackDestination.channelNum, - isEmoji: true, - replyID: message.messageId - ) - Task { @MainActor in - self.context.refresh(tapBackDestination.managedObject, mergeChanges: true) - } - } catch { - Logger.services.warning("Failed to send tapback.") - } - } - } label: { - Text(tb.description) - Image(uiImage: tb.emojiString.image()!) - } - } + Button("Tapback") { + isShowingTapbackInput = true } Button(action: onReply) { diff --git a/Meshtastic/Views/Messages/MessageText.swift b/Meshtastic/Views/Messages/MessageText.swift index 28df8fba..98734b24 100644 --- a/Meshtastic/Views/Messages/MessageText.swift +++ b/Meshtastic/Views/Messages/MessageText.swift @@ -27,13 +27,14 @@ struct MessageText: View { // State for handling channel URL sheet @State private var saveChannelLink: SaveChannelLinkData? @State private var isShowingDeleteConfirmation = false + @State private var isShowingTapbackInput = false + @State private var tapbackText = "" var body: some View { SessionReplayPrivacyView(textAndInputPrivacy: .maskAll) { - let markdownText = LocalizedStringKey(message.messagePayloadMarkdown ?? (message.messagePayload ?? "EMPTY MESSAGE")) - return Text(markdownText) + Text(markdownText) .tint(Self.linkBlue) .padding(.vertical, 10) .padding(.horizontal, 8) @@ -91,6 +92,7 @@ struct MessageText: View { tapBackDestination: tapBackDestination, isCurrentUser: isCurrentUser, isShowingDeleteConfirmation: $isShowingDeleteConfirmation, + isShowingTapbackInput: $isShowingTapbackInput, onReply: onReply ) } @@ -132,6 +134,36 @@ struct MessageText: View { .presentationDetents([.large]) .presentationDragIndicator(.visible) } + .sheet(isPresented: $isShowingTapbackInput) { + TapbackInputView( + text: $tapbackText, + isPresented: $isShowingTapbackInput, + onEmojiSelected: { emoji in + Task { + do { + try await accessoryManager.sendMessage( + message: emoji, + toUserNum: tapBackDestination.userNum, + channel: tapBackDestination.channelNum, + isEmoji: true, + replyID: message.messageId + ) + Task { @MainActor in + switch tapBackDestination { + case let .channel(channel): + context.refresh(channel, mergeChanges: true) + case let .user(user): + context.refresh(user, mergeChanges: true) + } + } + } catch { + Logger.services.warning("Failed to send tapback.") + } + } + isShowingTapbackInput = false + } + ) + } .confirmationDialog( "Are you sure you want to delete this message?", isPresented: $isShowingDeleteConfirmation, diff --git a/Meshtastic/Views/Messages/TapbackInputView.swift b/Meshtastic/Views/Messages/TapbackInputView.swift new file mode 100644 index 00000000..4b961295 --- /dev/null +++ b/Meshtastic/Views/Messages/TapbackInputView.swift @@ -0,0 +1,108 @@ +import SwiftUI +import UIKit + +struct TapbackInputView: View { + @Binding var text: String + @Binding var isPresented: Bool + let onEmojiSelected: (String) -> Void + + var body: some View { + NavigationView { + VStack(spacing: 0) { + EmojiOnlyTextField( + text: $text, + placeholder: "Tap to enter emoji", + onBecomeFirstResponder: { + // Text field will automatically become first responder + }, + onKeyboardTypeChanged: { shouldDismiss in + // Dismiss if keyboard switched away from emoji + if shouldDismiss { + isPresented = false + } + }, + onKeyboardDismissed: { + // Dismiss sheet when keyboard is dismissed + isPresented = false + } + ) + .frame(height: 50) + .padding(.horizontal) + .background( + RoundedRectangle(cornerRadius: 10) + .strokeBorder(.tertiary, lineWidth: 1) + .background(RoundedRectangle(cornerRadius: 10).fill(Color(.systemBackground))) + ) + .padding(.horizontal) + .padding(.top, 8) + .onChange(of: text) { oldValue, newValue in + // Extract first emoji character and send it + if !newValue.isEmpty, let firstEmoji = extractFirstEmoji(from: newValue) { + onEmojiSelected(firstEmoji) + // Clear the text box after getting the emoji + text = "" + } + } + } + .navigationTitle("Tapback") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button("Cancel") { + isPresented = false + } + } + } + } + .presentationDetents([.height(120)]) + } + + private func extractFirstEmoji(from string: String) -> String? { + // Extract the first emoji character(s) - handle both single and multi-scalar emojis + guard !string.isEmpty else { return nil } + + // Try to get the first character + let firstChar = string[string.startIndex] + + // Check if it's an emoji using the existing extension + if firstChar.isEmoji { + // For multi-scalar emojis (like emojis with skin tones), we need to find the full emoji sequence + var emojiEnd = string.index(after: string.startIndex) + + // Check if there are continuation scalars (for emojis with skin tones, variation selectors, etc.) + while emojiEnd < string.endIndex { + let nextChar = string[emojiEnd] + // Check if this is a continuation (variation selector, skin tone modifier, zero-width joiner, etc.) + if let scalar = nextChar.unicodeScalars.first, + (scalar.properties.isVariationSelector || + scalar.value == 0xFE0F || // Variation selector + (scalar.value >= 0x1F3FB && scalar.value <= 0x1F3FF) || // Skin tone modifiers + scalar.value == 0x200D) { // Zero-width joiner + emojiEnd = string.index(after: emojiEnd) + } else if nextChar.isEmoji { + // If it's another emoji, include it (for compound emojis like flags) + emojiEnd = string.index(after: emojiEnd) + } else { + break + } + } + + return String(string[string.startIndex.. Date: Sun, 4 Jan 2026 20:28:40 -0800 Subject: [PATCH 10/15] Call clearStaleNodes at start of sendWantConfig (#1535) --- Localizable.xcstrings | 4 ++++ Meshtastic/Accessory/Accessory Manager/AccessoryManager.swift | 2 ++ Meshtastic/Extensions/UserDefaults.swift | 4 ++++ Meshtastic/Views/Settings/AppSettings.swift | 2 +- 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 877f87f1..66249be2 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -16733,6 +16733,7 @@ } }, "Favorited and ignored nodes are always retained. Nodes without PKC keys are cleared from the app database on the schedule set by the user, nodes with PKC keys are cleared only if the interval is set to 7 days or longer. This feature only purges nodes from the app that are not stored in the device node database." : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -16759,6 +16760,9 @@ } } } + }, + "Favorited and ignored nodes are always retained. Other nodes are cleared from the app database on the schedule set by the user. (Nodes with PKC keys are always retained for at least 7 days.) This feature only purges nodes from the app that are not stored in the device node database." : { + }, "Favorites" : { "localizations" : { diff --git a/Meshtastic/Accessory/Accessory Manager/AccessoryManager.swift b/Meshtastic/Accessory/Accessory Manager/AccessoryManager.swift index 07513866..fc352614 100644 --- a/Meshtastic/Accessory/Accessory Manager/AccessoryManager.swift +++ b/Meshtastic/Accessory/Accessory Manager/AccessoryManager.swift @@ -196,6 +196,8 @@ class AccessoryManager: ObservableObject, MqttClientProxyManagerDelegate { Logger.transport.error("Unable to send wantConfig (config): No device connected") return } + + _ = clearStaleNodes(nodeExpireDays: Int(UserDefaults.purgeStaleNodeDays), context: self.context) try await withTaskCancellationHandler { var toRadio: ToRadio = ToRadio() diff --git a/Meshtastic/Extensions/UserDefaults.swift b/Meshtastic/Extensions/UserDefaults.swift index 82e67773..12bd86ee 100644 --- a/Meshtastic/Extensions/UserDefaults.swift +++ b/Meshtastic/Extensions/UserDefaults.swift @@ -80,6 +80,7 @@ extension UserDefaults { case showDeviceOnboarding case usageDataAndCrashReporting case autoconnectOnDiscovery + case purgeStaleNodeDays case manualConnections case testIntEnum } @@ -178,6 +179,9 @@ extension UserDefaults { @UserDefault(.autoconnectOnDiscovery, defaultValue: true) static var autoconnectOnDiscovery: Bool + @UserDefault(.purgeStaleNodeDays, defaultValue: 0) + static var purgeStaleNodeDays: Double + @UserDefault(.testIntEnum, defaultValue: .one) static var testIntEnum: TestIntEnum diff --git a/Meshtastic/Views/Settings/AppSettings.swift b/Meshtastic/Views/Settings/AppSettings.swift index 2f10c2af..243325b1 100644 --- a/Meshtastic/Views/Settings/AppSettings.swift +++ b/Meshtastic/Views/Settings/AppSettings.swift @@ -120,7 +120,7 @@ struct AppSettings: View { Text("180") } } - Text("Favorited and ignored nodes are always retained. Nodes without PKC keys are cleared from the app database on the schedule set by the user, nodes with PKC keys are cleared only if the interval is set to 7 days or longer. This feature only purges nodes from the app that are not stored in the device node database.") + Text("Favorited and ignored nodes are always retained. Other nodes are cleared from the app database on the schedule set by the user. (Nodes with PKC keys are always retained for at least 7 days.) This feature only purges nodes from the app that are not stored in the device node database.") .foregroundStyle(.secondary) .font(idiom == .phone ? .caption : .callout) } From 5c22b8b6e0176f4927bfc79234dabe109b215edf Mon Sep 17 00:00:00 2001 From: Benjamin Faershtein <119711889+RCGV1@users.noreply.github.com> Date: Sun, 4 Jan 2026 20:38:17 -0800 Subject: [PATCH 11/15] NFC Tag contact (#1537) --- Localizable.xcstrings | 17 ++- Meshtastic/Info.plist | 2 + Meshtastic/Meshtastic.entitlements | 4 + Meshtastic/Router/NavigationState.swift | 1 + Meshtastic/Views/Settings/Settings.swift | 9 ++ Meshtastic/Views/Settings/Tools.swift | 164 +++++++++++++++++++++++ 6 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 Meshtastic/Views/Settings/Tools.swift diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 66249be2..ca97d123 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -10693,6 +10693,10 @@ } } }, + "Create Node Contact NFC Tag" : { + "comment" : "A section header that instructs the user to create a contact NFC tag.", + "isCommentAutoGenerated" : true + }, "Create Waypoint" : { "localizations" : { "de" : { @@ -27747,6 +27751,10 @@ } } }, + "Node Name: %@" : { + "comment" : "A text label displaying the name of the connected node.", + "isCommentAutoGenerated" : true + }, "Node Number" : { "localizations" : { "de" : { @@ -44139,6 +44147,9 @@ } } } + }, + "Tools" : { + }, "Topic: %@" : { "localizations" : { @@ -48733,6 +48744,10 @@ } } }, + "Write Contact to NFC Tag" : { + "comment" : "A button that writes a contact to an NFC tag.", + "isCommentAutoGenerated" : true + }, "x" : { "localizations" : { "es" : { @@ -49444,4 +49459,4 @@ } }, "version" : "1.1" -} +} \ No newline at end of file diff --git a/Meshtastic/Info.plist b/Meshtastic/Info.plist index 863fb0e9..c2cbcdd8 100644 --- a/Meshtastic/Info.plist +++ b/Meshtastic/Info.plist @@ -97,6 +97,8 @@ LSSupportsOpeningDocumentsInPlace + NFCReaderUsageDescription + We use NFC tags to share node contacts NSBluetoothAlwaysUsageDescription We use bluetooth to connect to nearby Meshtastic Devices NSBluetoothPeripheralUsageDescription diff --git a/Meshtastic/Meshtastic.entitlements b/Meshtastic/Meshtastic.entitlements index 4dbdb836..a35e74ee 100644 --- a/Meshtastic/Meshtastic.entitlements +++ b/Meshtastic/Meshtastic.entitlements @@ -9,6 +9,10 @@ com.apple.developer.carplay-communication + com.apple.developer.nfc.readersession.formats + + TAG + com.apple.developer.usernotifications.critical-alerts com.apple.developer.weatherkit diff --git a/Meshtastic/Router/NavigationState.swift b/Meshtastic/Router/NavigationState.swift index 48a97b93..8c2ff6b3 100644 --- a/Meshtastic/Router/NavigationState.swift +++ b/Meshtastic/Router/NavigationState.swift @@ -52,6 +52,7 @@ enum SettingsNavigationState: String { case debugLogs case appFiles case firmwareUpdates + case tools } struct NavigationState: Hashable { diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index d3d15a66..25b0e75a 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -355,6 +355,13 @@ struct Settings: View { Image(systemName: "gearshape") } } + NavigationLink(value: SettingsNavigationState.tools) { + Label { + Text("Tools") + } icon: { + Image(systemName: "hammer") + } + } NavigationLink(value: SettingsNavigationState.routes) { Label { Text("Routes") @@ -521,6 +528,8 @@ struct Settings: View { AppData() case .firmwareUpdates: Firmware(node: node) + case .tools: + Tools() } } .onChange(of: UserDefaults.preferredPeripheralNum ) { _, newConnectedNode in diff --git a/Meshtastic/Views/Settings/Tools.swift b/Meshtastic/Views/Settings/Tools.swift new file mode 100644 index 00000000..75e439de --- /dev/null +++ b/Meshtastic/Views/Settings/Tools.swift @@ -0,0 +1,164 @@ +// +// Tools.swift +// Meshtastic +// +// Created by Benjamin Faershtein on 12/31/25. +// + +import SwiftUI +import CoreNFC +import MeshtasticProtobufs +import OSLog + +struct Tools: View { + @EnvironmentObject var accessoryManager: AccessoryManager + @Environment(\.managedObjectContext) var context + + @StateObject private var nfcReader = NFCReader() + + var connectedNode: NodeInfoEntity? { + if let num = accessoryManager.activeDeviceNum { + return getNodeInfo(id: num, context: context) + } + return nil + } + + var qrString: String { + var contact = SharedContact() + contact.nodeNum = UInt32(connectedNode?.num ?? 0) + contact.user = connectedNode?.toProto().user ?? User() + contact.manuallyVerified = true + + do { + let contactString = try contact.serializedData().base64EncodedString() + return "https://meshtastic.org/v/#" + contactString.base64ToBase64url() + } catch { + Logger.services.error("Error serializing contact: \(error)") + return "" + } + } + + var body: some View { + VStack{ + List { + Section(header: Text("Create Node Contact NFC Tag")) { + if let node = connectedNode { + Text("Node Name: \(node.user?.longName ?? "Unknown")") + + Button { + nfcReader.scan(theActualData: qrString) + } label: { + Label("Write Contact to NFC Tag", systemImage: "tag") + } + .disabled(qrString.isEmpty) + } + } + } + } + .navigationTitle("Tools") + .navigationBarTitleDisplayMode(.inline) + } +} + +#Preview { + Tools() +} + +final class NFCReader: NSObject, ObservableObject, NFCNDEFReaderSessionDelegate { + + private let logger = Logger(subsystem: "org.meshtastic.app", category: "NFC") + private var payloadString = "" + private var session: NFCNDEFReaderSession? + + func scan(theActualData: String) { + payloadString = theActualData + + session = NFCNDEFReaderSession( + delegate: self, + queue: nil, + invalidateAfterFirstRead: false + ) + + session?.alertMessage = "Hold your iPhone near the NFC tag." + session?.begin() + } + + func readerSessionDidBecomeActive(_ session: NFCNDEFReaderSession) { + logger.debug("NFC session became active") + } + + func readerSession(_ session: NFCNDEFReaderSession, + didInvalidateWithError error: Error) { + logger.error("NFC session invalidated: \(error.localizedDescription)") + } + + func readerSession(_ session: NFCNDEFReaderSession, + didDetectNDEFs messages: [NFCNDEFMessage]) { + } + + func readerSession(_ session: NFCNDEFReaderSession, + didDetect tags: [NFCNDEFTag]) { + + guard tags.count == 1, let tag = tags.first else { + session.alertMessage = "More than one tag detected. Please present only one." + DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(500)) { + session.restartPolling() + } + return + } + + session.connect(to: tag) { error in + if let error { + self.logger.error("Failed to connect to tag: \(error.localizedDescription)") + session.alertMessage = "Failed to connect to tag." + session.invalidate() + return + } + + tag.queryNDEFStatus { status, _, error in + if let error { + self.logger.error("Failed to query NDEF status: \(error.localizedDescription)") + session.alertMessage = "Failed to read tag." + session.invalidate() + return + } + + switch status { + case .notSupported: + self.logger.error("Tag does not support NDEF") + session.alertMessage = "Tag does not support NDEF." + session.invalidate() + + case .readOnly: + self.logger.error("Tag is read-only") + session.alertMessage = "Tag is read-only." + session.invalidate() + + case .readWrite: + guard let payload = + NFCNDEFPayload.wellKnownTypeURIPayload( + string: self.payloadString + ) else { + self.logger.error("Invalid NDEF payload") + session.alertMessage = "Invalid payload." + session.invalidate() + return + } + + let message = NFCNDEFMessage(records: [payload]) + + tag.writeNDEF(message) { error in + if let error { + self.logger.error("Failed to write NDEF: \(error.localizedDescription)") + session.alertMessage = "Failed to write tag." + } else { + self.logger.info("Successfully wrote NFC tag") + session.alertMessage = "NFC tag written successfully." + } + session.invalidate() + } + } + } + } + } +} From 487f24b99a4f3d0b4491ee7a2c86dcffb7f62c7f Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 4 Jan 2026 20:38:39 -0800 Subject: [PATCH 12/15] Accessorymanager background discovery (#1542) * Don't add new BLE devices to the device list in the backgournd * Bump version * Update Meshtastic/MeshtasticApp.swift Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update Meshtastic/MeshtasticApp.swift Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Accessory Manager/AccessoryManager+Discovery.swift | 8 ++++++-- .../Accessory/Accessory Manager/AccessoryManager.swift | 1 + Meshtastic/MeshtasticApp.swift | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Meshtastic/Accessory/Accessory Manager/AccessoryManager+Discovery.swift b/Meshtastic/Accessory/Accessory Manager/AccessoryManager+Discovery.swift index 831ffe30..266b6945 100644 --- a/Meshtastic/Accessory/Accessory Manager/AccessoryManager+Discovery.swift +++ b/Meshtastic/Accessory/Accessory Manager/AccessoryManager+Discovery.swift @@ -52,8 +52,12 @@ extension AccessoryManager { existing.rssi = newDevice.rssi self.devices[index] = existing } else { - // This is a new device, add it to our list - self.devices.append(newDevice) + // This is a new device, add it to our list if we are in the foreground + if !(self.isInBackground) { + self.devices.append(newDevice) + } else { + Logger.transport.debug("🔎 [Discovery] Found a new device but not in the foreground, not adding to our list: peripheral \(newDevice.name)") + } } if self.shouldAutomaticallyConnectToPreferredPeripheral, diff --git a/Meshtastic/Accessory/Accessory Manager/AccessoryManager.swift b/Meshtastic/Accessory/Accessory Manager/AccessoryManager.swift index fc352614..91f13287 100644 --- a/Meshtastic/Accessory/Accessory Manager/AccessoryManager.swift +++ b/Meshtastic/Accessory/Accessory Manager/AccessoryManager.swift @@ -135,6 +135,7 @@ class AccessoryManager: ObservableObject, MqttClientProxyManagerDelegate { @Published var lastConnectionError: Error? @Published var isConnected: Bool = false @Published var isConnecting: Bool = false + @Published var isInBackground: Bool = false var activeConnection: (device: Device, connection: any Connection)? diff --git a/Meshtastic/MeshtasticApp.swift b/Meshtastic/MeshtasticApp.swift index d60ed940..5c42dd22 100644 --- a/Meshtastic/MeshtasticApp.swift +++ b/Meshtastic/MeshtasticApp.swift @@ -193,6 +193,7 @@ struct MeshtasticAppleApp: App { } } .onChange(of: scenePhase) { (_, newScenePhase) in + accessoryManager.isInBackground = (newScenePhase == .background) switch newScenePhase { case .background: Logger.services.info("🎬 [App] Scene is in the background") From 2caba22a352ab178d1b1fdffd7c1e26b9a20a78b Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 4 Jan 2026 20:52:40 -0800 Subject: [PATCH 13/15] Revert "Full translation into Spanish (#1529)" (#1543) This reverts commit f25fdfb89fba70d22cf1d281c62f956f94d6343c. --- Localizable.xcstrings | 7024 +---------------------------------------- 1 file changed, 11 insertions(+), 7013 deletions(-) diff --git a/Localizable.xcstrings b/Localizable.xcstrings index ca97d123..b47fe554 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -6,12 +6,6 @@ }, "\t%@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "\t%@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -47,12 +41,6 @@ }, " %@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : " %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -146,12 +134,6 @@ }, ": %d" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : ": %d" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -187,12 +169,6 @@ }, "(Re)define PIN_GPS_EN for your board." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "(Re)defina PIN_GPS_EN para su placa." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -227,12 +203,6 @@ }, "%@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -356,12 +326,6 @@ "value" : "%1$@ - %2$@ Towards %3$@ Back" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ - %@ Hacia %@ Atrás" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -402,12 +366,6 @@ "value" : "%@ - Keine Antwort" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ - Sin respuesta" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -448,12 +406,6 @@ "value" : "%@ - Nicht gesendet" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ - No enviado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -576,12 +528,6 @@ "value" : "%1$@ %2$lld" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ %lld" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -623,12 +569,6 @@ "value" : "%@ entfernt" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ lejos" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -669,12 +609,6 @@ "value" : "%@ kann bis zu %@ Byte lang sein." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ puede tener hasta %@ bytes de longitud." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -709,12 +643,6 @@ }, "%@ Channels?" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ ¿Canales?" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -749,12 +677,6 @@ }, "%@ config data was requested via PKC admin but no response has been returned from the remote node." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Se solicitaron %@ datos de configuración a través del administrador de PKC pero no se obtuvo respuesta del nodo remoto." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -765,12 +687,6 @@ }, "%@ dB" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@dB" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -852,12 +768,6 @@ "value" : "%1$@: %2$lld / %3$lld" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@: %lld / %lld" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -893,12 +803,6 @@ }, "%@%%" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@%%" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -933,12 +837,6 @@ }, "%@°F" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@°F" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -973,12 +871,6 @@ }, "%@mA" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@mA" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -1007,12 +899,6 @@ }, "%@V" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@V" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -1041,12 +927,6 @@ }, "%d" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%d" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -1099,12 +979,6 @@ } } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%d saltos" - } - }, "it" : { "variations" : { "plural" : { @@ -1187,12 +1061,6 @@ }, "%d%%" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%d%%" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -1227,12 +1095,6 @@ }, "%f%%" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%f%%" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -1243,12 +1105,6 @@ }, "%lf" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lf" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -1283,12 +1139,6 @@ }, "%lld" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -1329,12 +1179,6 @@ "value" : "%1$lld %2$@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld %@" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -1381,12 +1225,6 @@ } } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld características" - } - }, "sr" : { "variations" : { "plural" : { @@ -1421,12 +1259,6 @@ "value" : "%lld oder weniger Hops entfernt" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld o menos salta de distancia" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -1455,12 +1287,6 @@ }, "%lld Readings Total" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld lecturas totales" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -1489,12 +1315,6 @@ }, "%lld Total Detection Events" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld total de eventos de detección" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -1523,12 +1343,6 @@ }, "%lld%%" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld%%" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -1569,12 +1383,6 @@ "value" : "%llddb Übertragungsleistung" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%llddb Potencia de transmisión" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -1615,12 +1423,6 @@ "value" : "%llddBm Übertragungsleistung" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "%llddBm Potencia de transmisión" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -1652,12 +1454,6 @@ }, "< 1%" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "< 1%" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -1690,32 +1486,12 @@ } } }, -<<<<<<< Updated upstream "⚠️ The configured value: (%@) is not one of the optimized options." : { "comment" : "A warning label below the picker, indicating that the selected update interval is not one of the optimized options.", "isCommentAutoGenerated" : true -======= - "⚠️ The configured value: (%@ seconds) is not one of the optimized options." : { - "comment" : "A text warning that the configured update interval is not one of the optimized options.", - "isCommentAutoGenerated" : true, - "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "⚠️ El valor configurado: (%@ segundos) no es una de las opciones optimizadas." - } - } - } ->>>>>>> Stashed changes }, "🦕 End of life Version 🦖 ☄️" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "🦕 Versión de fin de vida 🦖 ☄️" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -1744,12 +1520,6 @@ }, "0" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "0" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -1761,12 +1531,6 @@ }, "1" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "1" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -1778,12 +1542,6 @@ }, "1 byte" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 byte" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -1818,12 +1576,6 @@ }, "1 hop away" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "1 salto de distancia" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -1858,12 +1610,6 @@ }, "2.4 Ghz" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "2,4 GHz" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -1892,12 +1638,6 @@ }, "7" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "7" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -1932,12 +1672,6 @@ }, "12 Hour Clock" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Reloj de 12 horas" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -1954,12 +1688,6 @@ }, "25" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "25" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -1994,12 +1722,6 @@ }, "50" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "50" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -2034,12 +1756,6 @@ }, "75" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "75" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -2074,12 +1790,6 @@ }, "100" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "100" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -2114,12 +1824,6 @@ }, "128 bit" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "128 bits" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -2154,12 +1858,6 @@ }, "180" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "180" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -2171,12 +1869,6 @@ }, "256 bit" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "256 bits" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -2211,12 +1903,6 @@ }, "A channel index of 0 indicates the primary channel where broadcast packets are sent from. Location data is broadcast from the first channel where it is enabled with firmware 2.7 forward." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Un índice de canal de 0 indica el canal principal desde donde se envían los paquetes de transmisión. Los datos de ubicación se transmiten desde el primer canal donde está habilitado con el firmware 2.7 en adelante." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -2227,12 +1913,6 @@ }, "A green lock means the channel is securely encrypted with either a 128 or 256 bit AES key." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Un candado verde significa que el canal está cifrado de forma segura con una clave AES de 128 o 256 bits." - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -2255,12 +1935,6 @@ "value" : "In a Meshtastic LoRa Mesh there are up to 8 channels. The first one is the Primary channel where most activity happens and is required. If you don't share your primary channel your first shared channel becomes the primary channel on the other network. It talks on its primary and your secondary channel. A channel with the name 'admin' controls nodes remotely. Other channels are for private groups, each with its own key." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Un código QR Meshtastic contiene la configuración de LoRa y los valores de canal necesarios para que las radios se comuniquen. Puede compartir una configuración de canal completa usando la opción Reemplazar canales; si elige Agregar canales, sus canales compartidos se agregarán a los canales de la radio receptora." - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -2319,12 +1993,6 @@ }, "A red open lock means the channel is not securely encrypted and is used for precise location data, it uses either no key at all or a 1 byte known key." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Un candado rojo abierto significa que el canal no está cifrado de forma segura y se utiliza para datos de ubicación precisos; no utiliza ninguna clave o utiliza una clave conocida de 1 byte." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -2335,12 +2003,6 @@ }, "A red open lock with a warning means the channel is not securely encrypted and is used for precise location data which is being uplinked to the internet via MQTT, it uses either no key at all or a 1 byte known key." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Un candado rojo abierto con una advertencia significa que el canal no está cifrado de forma segura y se utiliza para datos de ubicación precisos que se conectan a Internet a través de MQTT; no utiliza ninguna clave o utiliza una clave conocida de 1 byte." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -2351,12 +2013,6 @@ }, "A Trace Route was sent, no response has been received." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Se envió una ruta de rastreo y no se recibió respuesta." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -2386,12 +2042,6 @@ "A yellow open lock lock means the channel is not securely encrypted but it not used for precise location data, it uses either no key at all or a 1 byte known key." : { "extractionState" : "stale", "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Un candado abierto amarillo significa que el canal no está cifrado de forma segura pero no se utiliza para datos de ubicación precisos; no utiliza ninguna clave o utiliza una clave conocida de 1 byte." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -2402,15 +2052,7 @@ }, "A yellow open lock means the channel is not securely encrypted but it is not used for precise location data, it uses either no key at all or a 1 byte known key." : { "comment" : "A description of a yellow open lock in the Channels Help view.", - "isCommentAutoGenerated" : true, - "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Un candado abierto amarillo significa que el canal no está cifrado de forma segura, pero no se utiliza para datos de ubicación precisos; no utiliza ninguna clave o utiliza una clave conocida de 1 byte." - } - } - } + "isCommentAutoGenerated" : true }, "About" : { "localizations" : { @@ -2420,12 +2062,6 @@ "value" : "Über Meshtastic" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Acerca de" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -2460,12 +2096,6 @@ "value" : "Über Meshtastic" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Acerca de Meshtastic" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -2506,12 +2136,6 @@ "value" : "Genauigkeit %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Exactitud %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -2540,12 +2164,6 @@ }, "Ack SNR: %@ dB" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Confirmación SNR: %@ dB" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -2574,12 +2192,6 @@ }, "Ack Time: %@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tiempo de confirmación: %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -2614,12 +2226,6 @@ "value" : "Bestätigt" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Admitido" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -2678,12 +2284,6 @@ }, "Acknowledged by another node" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Reconocido por otro nodo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -2718,12 +2318,6 @@ "value" : "Aktionen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Comportamiento" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -2758,12 +2352,6 @@ "value" : "Aktiv" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Activo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -2798,12 +2386,6 @@ "value" : "Aktivität" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Actividad" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -2844,12 +2426,6 @@ "value" : "ADC Override" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Anulación del ADC" - } - }, "he" : { "stringUnit" : { "state" : "translated", @@ -2896,12 +2472,6 @@ }, "Add Channel" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Agregar canal" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -2930,12 +2500,6 @@ }, "Add Channels" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Agregar canales" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -2964,12 +2528,6 @@ }, "Add Contact" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Agregar contacto" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -2992,12 +2550,6 @@ }, "Add Meshtastic Node %@ as a contact" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Agregar Meshtastic Node %@ como contacto" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -3032,12 +2584,6 @@ "value" : "Zu Favoriten hinzufügen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Añadir a favoritos" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -3066,12 +2612,6 @@ }, "Additional help" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ayuda adicional" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -3106,12 +2646,6 @@ }, "Address" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dirección" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -3146,12 +2680,6 @@ }, "Admin Keys" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Claves de administrador" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -3168,12 +2696,6 @@ }, "Administration" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Administración" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -3208,12 +2730,6 @@ }, "Administration Enabled" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Administración habilitada" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -3236,12 +2752,6 @@ }, "Advanced" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Avanzado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -3276,12 +2786,6 @@ }, "Advanced Device GPS" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dispositivo GPS avanzado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -3317,12 +2821,6 @@ "Advanced GPIO Options" : { "extractionState" : "stale", "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Opciones GPIO avanzadas" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -3357,12 +2855,6 @@ }, "Advanced Position Flags" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Banderas de posición avanzadas" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -3403,12 +2895,6 @@ "value" : "Nach" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Después" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -3479,12 +2965,6 @@ } } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Después de %lld días" - } - }, "ja" : { "variations" : { "plural" : { @@ -3531,12 +3011,6 @@ "value" : "Nach dem Ändern der Einstellungen wird das Gerät neu starten." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Después de guardar los valores de configuración, el nodo se reiniciará." - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -3601,12 +3075,6 @@ "value" : "Nachmittag" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tarde" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -3641,12 +3109,6 @@ "value" : "Airtime" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tiempo en antena" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -3705,12 +3167,6 @@ }, "Alert" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Alerta" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -3739,12 +3195,6 @@ }, "Alert GPIO buzzer when receiving a bell" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Alerta al zumbador GPIO al recibir una campana" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -3779,12 +3229,6 @@ }, "Alert GPIO buzzer when receiving a message" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Alerta al zumbador GPIO al recibir un mensaje" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -3813,12 +3257,6 @@ }, "Alert GPIO vibra motor when receiving a bell" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Alertar al motor de vibración GPIO al recibir una campana" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -3853,12 +3291,6 @@ }, "Alert GPIO vibra motor when receiving a message" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Alertar al motor de vibración GPIO al recibir un mensaje" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -3887,12 +3319,6 @@ }, "Alert when receiving a bell" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Alerta al recibir una campana" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -3927,12 +3353,6 @@ }, "Alert when receiving a message" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Alerta al recibir un mensaje" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -3967,12 +3387,6 @@ "value" : "Alle" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Todo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -4007,12 +3421,6 @@ }, "Allow Position Requests" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Permitir solicitudes de posición" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -4041,12 +3449,6 @@ }, "Alt" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Alt." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -4081,12 +3483,6 @@ "value" : "Höhe" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Altitud" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -4121,12 +3517,6 @@ "value" : "Höhe %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Altitud %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -4155,12 +3545,6 @@ }, "Altitude Geoidal Separation" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Separación geoideal de altitud" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -4189,12 +3573,6 @@ }, "Altitude is Mean Sea Level" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "La altitud es el nivel medio del mar." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -4229,12 +3607,6 @@ "value" : "Immer an" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Siempre encendido" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -4299,12 +3671,6 @@ "value" : "Immer nach Norden zeigen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Apunta siempre al norte" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -4339,12 +3705,6 @@ "value" : "Ambientebeleuchtung" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Iluminación ambiental" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -4403,12 +3763,6 @@ "value" : "Ambientebeleuchtungskonfiguration" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración de iluminación ambiental" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -4461,12 +3815,6 @@ }, "Ambient Lighting module config received: %@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración del módulo de iluminación ambiental recibida: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -4531,12 +3879,6 @@ "value" : "Ein quelloffenes, netzunabhängiges, dezentrales Mesh-Netzwerk, das auf kostengünstigen, stromsparenden Funkgeräten läuft." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Una red de malla descentralizada, fuera de la red y de código abierto que funciona con radios asequibles y de bajo consumo." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -4569,29 +3911,8 @@ } } }, -<<<<<<< Updated upstream -======= - "Anonymous Usage and Crash data" : { - "comment" : "A description of how the app collects and uses data about its usage and crashes. It emphasizes that this data is anonymous and non-personally identifiable.", - "isCommentAutoGenerated" : true, - "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Datos anónimos de uso y fallos" - } - } - } - }, ->>>>>>> Stashed changes "Any missed messages will be delivered again." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cualquier mensaje perdido se entregará nuevamente." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -4632,12 +3953,6 @@ "value" : "Client (Standard) - Mit App verbundener Client." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Aplicación conectada o dispositivo de mensajería independiente." - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -4702,12 +4017,6 @@ "value" : "App-Daten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Datos de la aplicación" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -4742,12 +4051,6 @@ }, "App Files" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Archivos de aplicación" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -4782,12 +4085,6 @@ }, "App Icon" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Icono de aplicación" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -4804,12 +4101,6 @@ "value" : "Mitteilungseinstellungen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Notificaciones de aplicaciones" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -4826,12 +4117,6 @@ "value" : "App-Einstellungen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración de la aplicación" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -4866,12 +4151,6 @@ }, "Apple Apps" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Aplicaciones de Apple" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -4912,12 +4191,6 @@ "value" : "Ungefährer Standort" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ubicación aproximada" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -4946,12 +4219,6 @@ }, "Are you sure you want to delete this message?" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "¿Estás seguro de que deseas eliminar este mensaje?" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -4992,12 +4259,6 @@ "value" : "Bist du sicher dass du den Knoten auf die Werkseinstellungen zurücksetzen willst?" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "¿Está seguro de que desea restablecer el nodo a los valores de fábrica?" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -5038,12 +4299,6 @@ "value" : "Bist Du sicher?" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "¿Está seguro?" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -5102,12 +4357,6 @@ }, "Australia / New Zealand" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Australia / Nueva Zelanda" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -5136,12 +4385,6 @@ }, "Automatically Connect" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Conectar automáticamente" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -5152,12 +4395,6 @@ }, "Automatically toggles to the next page on the screen like a carousel, based the specified interval." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cambia automáticamente a la página siguiente en la pantalla como un carrusel, según el intervalo especificado." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -5198,12 +4435,6 @@ "value" : "Verfügbare Modem-Voreinstellungen, Standard ist „Long Range - Fast“." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Presets de módem disponibles, el valor predeterminado es Long Fast." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -5244,12 +4475,6 @@ "value" : "Geräte in der Nähe" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Radios disponibles" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -5314,12 +4539,6 @@ "value" : "Zurück" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Atrás" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -5378,12 +4597,6 @@ }, "Backup" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Respaldo" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -5400,12 +4613,6 @@ }, "Backup your private key to your iCloud keychain." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Haga una copia de seguridad de su clave privada en su llavero de iCloud." - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -5422,12 +4629,6 @@ }, "Bad" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Malo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -5456,12 +4657,6 @@ }, "Bad Request" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Solicitud incorrecta" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -5526,12 +4721,6 @@ "value" : "Bandbreite" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ancho de banda" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -5566,12 +4755,6 @@ }, "Bar" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bar" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -5606,12 +4789,6 @@ }, "Bar Series" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Serie de barras" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -5646,12 +4823,6 @@ }, "Barometric Pressure" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Presión barométrica" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -5692,12 +4863,6 @@ "value" : "Batterie" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Batería" - } - }, "he" : { "stringUnit" : { "state" : "translated", @@ -5757,12 +4922,6 @@ "value" : "Batterie Ladung" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nivel de batería" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -5828,12 +4987,6 @@ "value" : "Batterie Ladung %" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "% del nivel de batería" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -5899,12 +5052,6 @@ "value" : "Batterie Ladung %d" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nivel de batería %d" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -5963,12 +5110,6 @@ }, "Baud" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "baudios" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -6015,12 +5156,6 @@ "value" : "Biken" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ciclismo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -6055,12 +5190,6 @@ }, "BLE" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "BLE" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -6101,12 +5230,6 @@ "value" : "Die Bluetooth Pin muss 6 Stellen lang sein." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "El pin BLE debe tener 6 dígitos." - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -6171,12 +5294,6 @@ "value" : "Bluetooth" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "bluetooth" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -6241,12 +5358,6 @@ "value" : "Bluetooth Konfiguration" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración de Bluetooth" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -6311,12 +5422,6 @@ "value" : "Bluetooth Konfiguration empfangen: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración de Bluetooth recibida: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -6377,12 +5482,6 @@ "comment" : "A heading displayed on a view that guides users to configure Bluetooth connectivity for the app.", "isCommentAutoGenerated" : true, "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Conectividad Bluetooth" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -6393,12 +5492,6 @@ }, "Bold Heading" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Encabezado en negrita" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -6409,12 +5502,6 @@ }, "Bold the heading text on the screen." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Negrita el texto del encabezado en la pantalla." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -6424,23 +5511,10 @@ } }, "Broadcast Device Metrics" : { - "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Métricas de dispositivos de transmisión" - } - } - } + }, "Broadcast Interval" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Intervalo de transmisión" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -6481,12 +5555,6 @@ "value" : "Sendet GPS-Positionspakete mit Priorität." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Transmite paquetes de posición GPS como prioridad." - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -6551,12 +5619,6 @@ "value" : "Sendet den Standort regelmäßig als Nachricht an den Standardkanal, um die Suche nach dem Gerät zu unterstützen." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Transmite la ubicación como mensaje al canal predeterminado con regularidad para ayudar con la recuperación del dispositivo." - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -6621,12 +5683,6 @@ "value" : "Sendet Telemetriepakete mit Priorität." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Transmite paquetes de telemetría como prioridad." - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -6685,12 +5741,6 @@ }, "Button GPIO" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Botón GPIO" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -6725,12 +5775,6 @@ }, "Buy Complete Radios" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Comprar Radios Completas" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -6765,12 +5809,6 @@ }, "Buzzer GPIO" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Zumbador GPIO" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -6805,12 +5843,6 @@ }, "By enabling this feature, you acknowledge and expressly consent to the transmission of your device’s real-time geographic location over the MQTT protocol without encryption. This location data may be used for purposes such as live map reporting, device tracking, and related telemetry functions." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Al habilitar esta función, usted reconoce y acepta expresamente la transmisión de la ubicación geográfica en tiempo real de su dispositivo a través del protocolo MQTT sin cifrado. Estos datos de ubicación se pueden utilizar para fines tales como informes de mapas en vivo, seguimiento de dispositivos y funciones de telemetría relacionadas." - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -6839,12 +5871,6 @@ "value" : "Bytes" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "bytes" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -6904,12 +5930,6 @@ "Bytes Used" : { "comment" : "VoiceOver value for bytes used", "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bytes utilizados" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -6932,12 +5952,6 @@ "value" : "Rufzeichen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Señal de llamada" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -6978,12 +5992,6 @@ "value" : "Das Rufzeichen darf nicht leer sein." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "El indicativo de llamada no debe estar vacío" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -7024,12 +6032,6 @@ "value" : "Abbrechen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cancelar" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -7088,12 +6090,6 @@ }, "Canned Message module config received: %@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración del módulo de mensajes predefinidos recibida: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -7158,12 +6154,6 @@ "value" : "Canned Messages" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mensajes enlatados" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -7228,12 +6218,6 @@ "value" : "Canned Messages Config" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración de mensajes predefinidos" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -7292,12 +6276,6 @@ }, "Canned Messages Messages Received For: %@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mensajes almacenados Mensajes recibidos para: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -7356,12 +6334,6 @@ }, "Carousel Interval" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Intervalo de carrusel" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -7396,12 +6368,6 @@ "value" : "Kategorien" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Categorías" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -7442,12 +6408,6 @@ "value" : "Kategorie" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Categoría" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -7476,12 +6436,6 @@ }, "Ch1 Current" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Corriente Ch1" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -7510,12 +6464,6 @@ }, "Ch1 Voltage" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Voltaje Ch1" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -7544,12 +6492,6 @@ }, "Ch2 Current" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Corriente Ch2" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -7578,12 +6520,6 @@ }, "Ch2 Voltage" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Voltaje Ch2" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -7612,12 +6548,6 @@ }, "Ch3 Current" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ch3 actual" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -7646,12 +6576,6 @@ }, "Ch3 Voltage" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Voltaje Ch3" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -7686,12 +6610,6 @@ "value" : "Kanal" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Canal" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -7750,12 +6668,6 @@ }, "Channel 0 Included" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Canal 0 incluido" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -7790,12 +6702,6 @@ }, "Channel 1" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Canal 1" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -7824,12 +6730,6 @@ }, "Channel 1 Included" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Canal 1 incluido" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -7864,12 +6764,6 @@ }, "Channel 2" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Canal 2" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -7898,12 +6792,6 @@ }, "Channel 2 Included" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Canal 2 incluido" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -7938,12 +6826,6 @@ }, "Channel 3" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Canal 3" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -7972,12 +6854,6 @@ }, "Channel 3 Included" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Canal 3 incluido" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -8012,12 +6888,6 @@ }, "Channel 4 Included" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Canal 4 incluido" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -8052,12 +6922,6 @@ }, "Channel 5 Included" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Canal 5 incluido" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -8092,12 +6956,6 @@ }, "Channel 6 Included" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Canal 6 incluido" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -8132,12 +6990,6 @@ }, "Channel 7 Included" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Canal 7 incluido" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -8172,12 +7024,6 @@ }, "Channel Details" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Detalles del canal" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -8212,12 +7058,6 @@ }, "Channel Name" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nombre del canal" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -8252,12 +7092,6 @@ }, "Channel number must be between 0 and 7." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "El número de canal debe estar entre 0 y 7." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -8292,12 +7126,6 @@ }, "Channel Role" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rol del canal" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -8332,12 +7160,6 @@ }, "Channel URL" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "URL del canal" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -8372,12 +7194,6 @@ "value" : "Kanalbelegung" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Utilización del canal" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -8436,12 +7252,6 @@ }, "Channel Utilization %@%%" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Utilización del canal %@%%" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -8476,12 +7286,6 @@ "value" : "Kanäle" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Canales" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -8540,12 +7344,6 @@ }, "Channels being added from the QR code did not save. When adding channels the names must be unique." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Los canales que se agregaron desde el código QR no se guardaron. Al agregar canales, los nombres deben ser únicos." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -8580,12 +7378,6 @@ }, "Channels Help" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ayuda de canales" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -8602,12 +7394,6 @@ }, "Chart" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cuadro" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -8642,12 +7428,6 @@ }, "CHG" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "CHG" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -8676,12 +7456,6 @@ }, "China" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Porcelana" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -8710,12 +7484,6 @@ }, "Chirpy" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Alegre" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -8726,12 +7494,6 @@ }, "Clear" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Claro" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -8772,12 +7534,6 @@ "value" : "App-Daten löschen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Borrar datos de la aplicación" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -8836,12 +7592,6 @@ }, "Clear Log" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Borrar registro" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -8882,12 +7632,6 @@ "value" : "Veraltete Knoten löschen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Borrar nodos obsoletos" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -8904,12 +7648,6 @@ }, "Client" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cliente" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -8954,12 +7692,6 @@ "value" : "Client - Versteckt" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cliente oculto" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -8994,12 +7726,6 @@ }, "Client History" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Historial del cliente" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -9034,12 +7760,6 @@ }, "Client History Request Sent" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Solicitud de historial del cliente enviada" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -9074,12 +7794,6 @@ }, "Client Mute" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Silencio del cliente" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -9114,12 +7828,6 @@ }, "Client options" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Opciones del cliente" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -9154,12 +7862,6 @@ }, "Clockwise Rotary Event" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Evento giratorio en el sentido de las agujas del reloj" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -9200,12 +7902,6 @@ "value" : "Schließen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cerca" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -9264,12 +7960,6 @@ }, "Coding Rate" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tasa de codificación" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -9310,12 +8000,6 @@ "value" : "Farbe" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Color" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -9356,12 +8040,6 @@ "value" : "Bleibe mit deinen Freunden und deiner Community in Verbindung, auch abseits vom Mobilfunknetz." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Comuníquese fuera de la red con sus amigos y su comunidad sin servicio celular." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -9372,12 +8050,6 @@ }, "Communicating" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Comunicado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -9406,12 +8078,6 @@ }, "Community Support" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Apoyo comunitario" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -9443,12 +8109,6 @@ }, "Config" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "configuración" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -9489,12 +8149,6 @@ "value" : "Konfiguration für: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración para: %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -9523,12 +8177,6 @@ }, "Configuration Presets" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Preajustes de configuración" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -9569,12 +8217,6 @@ "value" : "Konfigurieren" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configurar" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -9605,12 +8247,6 @@ "comment" : "Button label to guide users to configure Bluetooth connectivity for the app.", "isCommentAutoGenerated" : true, "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configurar la conectividad Bluetooth" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -9623,12 +8259,6 @@ "comment" : "Button label to configure local network access permissions.", "isCommentAutoGenerated" : true, "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configurar el acceso a la red local" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -9645,12 +8275,6 @@ "value" : "Standortberechtigungen konfigurieren" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configurar permisos de ubicación" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -9667,12 +8291,6 @@ "value" : "Mitteilungsberechtigungen konfigurieren" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configurar permisos de notificación" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -9683,12 +8301,6 @@ }, "Confirm" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Confirmar" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -9717,12 +8329,6 @@ }, "Connect" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Conectar" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -9745,12 +8351,6 @@ "value" : "Verbunden mit einem Knoten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Conectarse a un nodo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -9779,12 +8379,6 @@ }, "Connect to MQTT via Proxy" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Conéctese a MQTT a través de Proxy" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -9807,12 +8401,6 @@ }, "Connect to new radio?" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "¿Conectar a una nueva radio?" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -9847,12 +8435,6 @@ "value" : "Derzeit verbunden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Conectado" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -9917,12 +8499,6 @@ "value" : "Verbunden mit Knoten %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nodo conectado %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -9951,12 +8527,6 @@ }, "Connected Radio" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Radio conectada" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -9991,12 +8561,6 @@ "value" : "Verbinde..." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Conectando. ." - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -10055,12 +8619,6 @@ }, "Connecting to a new radio will clear all app data on the phone." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Al conectarse a una nueva radio se borrarán todos los datos de la aplicación en el teléfono." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -10095,12 +8653,6 @@ "value" : "Verbindungsversuch %lld von 10" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Intento de conexión %lld de 10" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -10135,12 +8687,6 @@ }, "Connection Name" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nombre de conexión" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -10151,12 +8697,6 @@ }, "Consent to Share Unencrypted Node Data via MQTT" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Consentimiento para compartir datos de nodos no cifrados a través de MQTT" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -10185,12 +8725,6 @@ "value" : "Kontaktfilter" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Filtros de contacto" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -10201,12 +8735,6 @@ }, "Contact URL" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "URL de contacto" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -10235,12 +8763,6 @@ "value" : "Kontakte (%@)" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Contactos (%@)" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -10299,12 +8821,6 @@ }, "Control Type" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tipo de control" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -10339,12 +8855,6 @@ }, "Controls the blinking LED on the device. For most devices this will control one of the up to 4 LEDS, the charger and GPS LEDs are not controllable." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Controla el LED parpadeante en el dispositivo. Para la mayoría de los dispositivos, esto controlará uno de los hasta 4 LED, los LED del cargador y del GPS no son controlables." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -10385,12 +8895,6 @@ "value" : "Konvexe Hülle" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Casco convexo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -10425,12 +8929,6 @@ "value" : "Koordinate" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Coordinar" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -10465,12 +8963,6 @@ "value" : "Koordinate %1$@, %2$@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Coordinar %@, %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -10511,12 +9003,6 @@ "value" : "Koordinaten:" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Coordenadas:" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -10551,12 +9037,6 @@ "value" : "Kopieren" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Copiar" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -10621,12 +9101,6 @@ "value" : "Knoten nicht gefunden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "No se pudo encontrar el nodo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -10661,12 +9135,6 @@ }, "Counter Clockwise Rotary Event" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Evento giratorio en sentido antihorario" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -10705,12 +9173,6 @@ "value" : "Wegpunkt erstellen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Crear punto de referencia" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -10745,12 +9207,6 @@ "value" : "Erstelle deine eigenen Netzwerke" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Crea tus propias redes" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -10767,12 +9223,6 @@ "value" : "Erstellt: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Creado: %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -10807,12 +9257,6 @@ "value" : "Kritische Hinweise" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Alertas críticas" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -10823,12 +9267,6 @@ }, "Current" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Actual" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -10863,12 +9301,6 @@ "value" : "Aktuelle Firmware Version: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Versión de firmware actual: %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -10909,12 +9341,6 @@ "value" : "Aktuelle Firmware Version: %1$@, neuste Firmware Version %2$@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Versión de firmware actual: %@, Última versión de firmware: %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -10955,12 +9381,6 @@ "value" : "Aktuell: %lld" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Actual: %lld" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -10989,12 +9409,6 @@ }, "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" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Actualmente, la forma recomendada de actualizar dispositivos ESP32 es utilizar el flash web en una computadora de escritorio desde un navegador basado en Chrome. No funciona en dispositivos móviles ni a través de BLE." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -11035,12 +9449,6 @@ "value" : "Datum" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Fecha" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -11069,12 +9477,6 @@ }, "Debug" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Depurar" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -11115,12 +9517,6 @@ "value" : "Fehlersuchprotokolle" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Registros de depuración" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -11149,12 +9545,6 @@ }, "Debug Logs%@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Registros de depuración%@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -11189,12 +9579,6 @@ "value" : "Standard" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Por defecto" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -11253,12 +9637,6 @@ }, "Default 128x64 screen layout" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Diseño de pantalla predeterminado de 128x64" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -11299,12 +9677,6 @@ "value" : "Löschen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Borrar" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -11363,12 +9735,6 @@ }, "Delete all config, keys and BLE bonds? " : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "¿Eliminar todas las configuraciones, claves y enlaces BLE?" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -11391,12 +9757,6 @@ }, "Delete all config? " : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "¿Eliminar todas las configuraciones?" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -11419,12 +9779,6 @@ }, "Delete all device metrics?" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "¿Eliminar todas las métricas del dispositivo?" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -11483,12 +9837,6 @@ }, "Delete all environment metrics?" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "¿Eliminar todas las métricas del entorno?" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -11523,12 +9871,6 @@ }, "Delete all pax data?" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "¿Eliminar todos los datos de los pasajeros?" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -11563,12 +9905,6 @@ }, "Delete all positions?" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "¿Eliminar todas las posiciones?" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -11597,12 +9933,6 @@ }, "Delete Message" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Eliminar mensaje" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -11631,12 +9961,6 @@ }, "Delete Messages" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Eliminar mensajes" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -11671,12 +9995,6 @@ "value" : "Knoten löschen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Eliminar nodo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -11711,12 +10029,6 @@ "value" : "Knoten löschen?" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "¿Eliminar nodo?" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -11745,12 +10057,6 @@ }, "Delete Power metrics?" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "¿Eliminar métricas de potencia?" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -11785,12 +10091,6 @@ "value" : "Beschreibung" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Descripción" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -11819,12 +10119,6 @@ }, "Description must be less than 100 bytes" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "La descripción debe tener menos de 100 bytes." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -11859,12 +10153,6 @@ }, "Details..." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Detalles..." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -11875,12 +10163,6 @@ }, "Detection" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Detección" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -11909,12 +10191,6 @@ }, "Detection event" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Evento de detección" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -11950,12 +10226,6 @@ "value" : "Detection Sensor" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sensor de detección" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -12014,12 +10284,6 @@ }, "Detection Sensor Config" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración del sensor de detección" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -12072,12 +10336,6 @@ }, "Detection Sensor Log" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Registro del sensor de detección" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -12112,12 +10370,6 @@ }, "Detection sensor messages are received as text messages. If you enable notifications you will recieve a notification for each detection message received and a corresponding unread message badge." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Los mensajes del sensor de detección se reciben como mensajes de texto. Si habilita las notificaciones, recibirá una notificación por cada mensaje de detección recibido y la correspondiente insignia de mensaje no leído." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -12152,12 +10404,6 @@ }, "Detection Sensor module config received: %@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración del módulo del sensor de detección recibida: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -12216,12 +10462,6 @@ }, "Developers" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Desarrolladores" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -12262,12 +10502,6 @@ "value" : "Gerät" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dispositivo" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -12332,12 +10566,6 @@ "value" : "Gerätekonfiguration" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración del dispositivo" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -12402,12 +10630,6 @@ "value" : "Gerätekonfiguration empfangen: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración del dispositivo recibida: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -12472,12 +10694,6 @@ "value" : "Gerätekonfiguration" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración del dispositivo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -12524,12 +10740,6 @@ "value" : "Geräte-GPS" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dispositivo GPS" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -12564,12 +10774,6 @@ }, "Device is managed by a mesh administrator, the user is unable to access any of the device settings." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "El dispositivo es administrado por un administrador de malla, el usuario no puede acceder a ninguna de las configuraciones del dispositivo." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -12610,12 +10814,6 @@ "value" : "Device Metadata empfangen von: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Metadatos del dispositivo recibidos de: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -12674,12 +10872,6 @@ }, "Device Metrics" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Métricas del dispositivo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -12714,12 +10906,6 @@ }, "Device Metrics Log" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Registro de métricas del dispositivo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -12760,12 +10946,6 @@ "value" : "Gerätemodell: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Modelo de dispositivo: %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -12799,23 +10979,10 @@ } }, "Device Options" : { - "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Opciones del dispositivo" - } - } - } + }, "Device Role" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Función del dispositivo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -12850,12 +11017,6 @@ }, "Device Screen" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pantalla del dispositivo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -12896,12 +11057,6 @@ "value" : "Gerät, das keine Pakete von anderen Geräten weiterleitet." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dispositivo que no reenvía paquetes desde otros dispositivos." - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -12966,12 +11121,6 @@ "value" : "Gerät, das nur bei Bedarf sendet, um nicht entdeckt zu werden oder Strom zu sparen." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dispositivo que solo transmite según sea necesario para sigilo o ahorro de energía." - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -13030,12 +11179,6 @@ }, "Dilution of precision (DOP) PDOP used by default" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dilución de precisión (DOP) PDOP utilizado por defecto" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -13070,12 +11213,6 @@ "value" : "Direkt" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Directo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -13116,12 +11253,6 @@ "value" : "Hilfe für Direktnachrichten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ayuda por mensaje directo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -13156,12 +11287,6 @@ }, "Direct Message Key" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tecla de mensaje directo" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -13184,12 +11309,6 @@ "value" : "Direktnachrichten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mensajes directos" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -13248,12 +11367,6 @@ }, "Direct messages are using the new public key infrastructure for encryption. Requires firmware version 2.5 or greater." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Los mensajes directos utilizan la nueva infraestructura de clave pública para el cifrado. Requiere versión de firmware 2.5 o superior." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -13288,12 +11401,6 @@ }, "Direct messages are using the shared key for the channel." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Los mensajes directos utilizan la clave compartida del canal." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -13334,12 +11441,6 @@ "value" : "Deaktiviert" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Desactivado" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -13404,12 +11505,6 @@ "value" : "Trennen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Desconectar" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -13468,12 +11563,6 @@ }, "Disconnect Node" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Desconectar nodo" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -13490,12 +11579,6 @@ }, "Disconnect the currently connected node" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Desconectar el nodo actualmente conectado" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -13518,12 +11601,6 @@ "value" : "Tastatur ausblenden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Despedir" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -13588,12 +11665,6 @@ "value" : "Display (Device Screen)" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mostrar" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -13652,12 +11723,6 @@ }, "Display Config" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración de pantalla" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -13722,12 +11787,6 @@ "value" : "Display Konfiguration empfangen: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración de pantalla recibida: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -13786,12 +11845,6 @@ }, "Display Fahrenheit" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mostrar grados Fahrenheit" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -13826,12 +11879,6 @@ }, "Display Mode" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Modo de visualización" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -13872,12 +11919,6 @@ "value" : "Darstellung der Entfernung zwischen deinem Handy und anderen Meshtastic-Knoten mit Positionsangabe." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Muestra la distancia entre tu teléfono y otros nodos Meshtastic con posiciones." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -13888,12 +11929,6 @@ }, "Display Units" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Unidades de visualización" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -13934,12 +11969,6 @@ "value" : "Distanz" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Distancia" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -13980,12 +12009,6 @@ "value" : "Distanzfilter" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Filtros de distancia" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -14002,12 +12025,6 @@ "value" : "Distanzmessungen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mediciones de distancia" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -14027,12 +12044,6 @@ "value" : "Dokumentation" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Documentación" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -14067,12 +12078,6 @@ }, "Done" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Hecho" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -14095,12 +12100,6 @@ }, "Double Tap as Button" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Toque dos veces como botón" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -14141,12 +12140,6 @@ "value" : "Runter" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Abajo" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -14205,12 +12198,6 @@ }, "Downlink Enabled" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enlace descendente habilitado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -14245,12 +12232,6 @@ }, "Drag & Drop Firmware Update" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Actualización de firmware de arrastrar y soltar" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -14285,12 +12266,6 @@ }, "Drag & Drop Firmware Update Documentation" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Documentación de actualización de firmware de arrastrar y soltar" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -14325,12 +12300,6 @@ }, "Drag & Drop is the recommended way to update firmware for NRF devices. If your iPhone or iPad is USB-C it will work with your regular USB-C charging cable, for lightning devices you need the Apple Lightning to USB camera adaptor." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Arrastrar y soltar es la forma recomendada de actualizar el firmware para dispositivos NRF. Si su iPhone o iPad es USB-C, funcionará con su cable de carga USB-C habitual; para dispositivos Lightning, necesita el adaptador de cámara Lightning a USB de Apple." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -14371,12 +12340,6 @@ "value" : "Fahren" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Conduciendo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -14411,12 +12374,6 @@ }, "Drop Pin in Maps" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Colocar pin en mapas" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -14451,12 +12408,6 @@ "value" : "Richte einfach private Mesh-Netzwerke für eine sichere und zuverlässige Kommunikation in abgelegenen Gebieten ein." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configure fácilmente redes de malla privadas para una comunicación segura y confiable en áreas remotas." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -14467,12 +12418,6 @@ }, "Echo" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Eco" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -14525,12 +12470,6 @@ }, "Editing Waypoint" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Editar punto de referencia" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -14565,12 +12504,6 @@ "value" : "Achtzehn Stunden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dieciocho horas" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -14635,12 +12568,6 @@ "value" : "Höhenunterschied" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Elev. Ganar" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -14669,12 +12596,6 @@ }, "Emoji" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "emojis" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -14709,12 +12630,6 @@ }, "Empty" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Vacío" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -14748,23 +12663,10 @@ } }, "Enable broadcasting device metrics to the mesh network. When disabled, metrics are only sent to connected clients." : { - "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Habilite la transmisión de métricas de dispositivos a la red de malla. Cuando está deshabilitado, las métricas solo se envían a los clientes conectados." - } - } - } + }, "Enable broadcasting packets via UDP over the local network." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Habilite la transmisión de paquetes a través de UDP a través de la red local." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -14799,12 +12701,6 @@ "value" : "Standortfreigabe aktivieren" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Habilitar compartir ubicación" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -14815,12 +12711,6 @@ }, "Enable Notifications" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Habilitar notificaciones" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -14855,12 +12745,6 @@ }, "Enable this device as a Store and Forward server. Requires an ESP32 device with PSRAM." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Habilite este dispositivo como servidor Store and Forward. Requiere un dispositivo ESP32 con PSRAM." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -14895,12 +12779,6 @@ "value" : "Aktiviert" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Activado" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -14965,12 +12843,6 @@ "value" : "Aktiviert automatische TAK-PLI-Übertragungen und verringert die Anzahl der Routineübertragungen." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Permite transmisiones automáticas de TAK PLI y reduce las transmisiones de rutina." - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -15029,12 +12901,6 @@ }, "Enables devices with native I2S audio output to use the RTTTL over speaker like a buzzer. T-Watch S3 and T-Deck for example have this capability." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Permite que los dispositivos con salida de audio I2S nativa utilicen el RTTTL a través del altavoz como un timbre. T-Watch S3 y T-Deck, por ejemplo, tienen esta capacidad." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -15075,12 +12941,6 @@ "value" : "Aktiviert den blauen Standort-Punkt für dein Handy in der Mesh-Karte." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Habilita el punto de ubicación azul para su teléfono en el mapa de malla." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -15091,12 +12951,6 @@ }, "Enables the detection sensor module, it needs to be enabled on both the node with the sensor, and any nodes that you want to receive detection sensor text messages or view the detection sensor log and chart." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Habilita el módulo del sensor de detección; debe estar habilitado tanto en el nodo con el sensor como en cualquier nodo en el que desee recibir mensajes de texto del sensor de detección o ver el registro y el gráfico del sensor de detección." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -15131,12 +12985,6 @@ }, "Enables the store and forward module." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Habilita el módulo de almacenamiento y reenvío." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -15165,12 +13013,6 @@ }, "Enabling Ethernet will disable the bluetooth connection to the app." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Al habilitar Ethernet se deshabilitará la conexión bluetooth a la aplicación." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -15193,12 +13035,6 @@ }, "Enabling WiFi will disable the bluetooth connection to the app." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Habilitar WiFi deshabilitará la conexión bluetooth a la aplicación." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -15209,12 +13045,6 @@ }, "Encoder Press Event" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Evento de prensa del codificador" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -15249,12 +13079,6 @@ "value" : "Verschlüsselt" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "cifrado" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -15320,12 +13144,6 @@ "value" : "Verschlüsseltes Senden fehlgeschlagen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Error de envío cifrado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -15354,12 +13172,6 @@ }, "Encryption Enabled" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cifrado habilitado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -15400,12 +13212,6 @@ "value" : "DFÜ-Modus aktivieren" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ingrese al modo DFU" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -15442,12 +13248,6 @@ "comment" : "A label for a text field where the user can enter a hostname or IP address and optionally a port number.", "isCommentAutoGenerated" : true, "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Introduzca el nombre de host[:puerto]" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -15464,12 +13264,6 @@ "value" : "Umgebung" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "ambiente" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -15510,12 +13304,6 @@ "value" : "Umgebung" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ambiente" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -15550,12 +13338,6 @@ }, "Environment Metrics" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Métricas ambientales" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -15589,23 +13371,10 @@ } }, "Environment Metrics Enabled" : { - "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Métricas de entorno habilitadas" - } - } - } + }, "Environment Metrics Log" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Registro de métricas ambientales" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -15639,14 +13408,7 @@ } }, "Environment Sensor Options" : { - "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Opciones de sensores ambientales" - } - } - } + }, "Erase all app data?" : { "localizations" : { @@ -15656,12 +13418,6 @@ "value" : "Alle App-Daten löschen?" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "¿Borrar todos los datos de la aplicación?" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -15702,12 +13458,6 @@ "value" : "Alle Geräte- und App-Daten löschen?" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "¿Borrar todos los datos del dispositivo y de las aplicaciones?" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -15742,12 +13492,6 @@ }, "Error: %@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Error: %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -15782,12 +13526,6 @@ }, "ESP 32 OTA update is a work in progress, click the button below to send your device a reboot into ota admin message." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "La actualización de ESP 32 OTA es un trabajo en progreso, haga clic en el botón a continuación para enviar su dispositivo a un reinicio en el mensaje de administrador de ota." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -15822,12 +13560,6 @@ }, "ESP32 Device Firmware Update" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Actualización del firmware del dispositivo ESP32" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -15862,12 +13594,6 @@ }, "Ethernet Options" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Opciones de Ethernet" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -15896,12 +13622,6 @@ }, "European Union 433MHz" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Unión Europea 433MHz" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -15930,12 +13650,6 @@ }, "European Union 868MHz" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Unión Europea 868MHz" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -15970,12 +13684,6 @@ "value" : "Abend" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Noche" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -16004,12 +13712,6 @@ }, "Exchange Positions" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Posiciones de intercambio" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -16044,12 +13746,6 @@ "value" : "Ausrufezeichen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Exclamación" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -16108,12 +13804,6 @@ }, "Expiration" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Vencimiento" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -16136,12 +13826,6 @@ "value" : "Zeitpunkt" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Expirar" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -16176,12 +13860,6 @@ "value" : "Automatisches Löschen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Vence" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -16210,12 +13888,6 @@ }, "Expires: %@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Vence: %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -16250,12 +13922,6 @@ "value" : "Exportieren" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Exportar" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -16290,12 +13956,6 @@ "value" : "Externe Benachrichtigung" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Notificación externa" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -16360,12 +14020,6 @@ "value" : "Einstellungen der externen Benachrichtigung" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración de notificación externa" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -16424,12 +14078,6 @@ }, "External Notification module config received: %@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración del módulo de notificación externa recibida: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -16494,12 +14142,6 @@ "value" : "Werkseinstellungen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Restablecimiento de fábrica" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -16528,12 +14170,6 @@ }, "Factory reset will delete device and app data." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "El restablecimiento de fábrica eliminará los datos del dispositivo y de la aplicación." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -16556,12 +14192,6 @@ }, "Failed to encode message content" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "No se pudo codificar el contenido del mensaje" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -16590,12 +14220,6 @@ }, "Failed to get a valid position to exchange" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "No se pudo obtener una posición válida para intercambiar" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -16624,12 +14248,6 @@ }, "Failed to get a valid position to exchange." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "No se pudo obtener una posición válida para intercambiar." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -16664,12 +14282,6 @@ "value" : "Ordentliche Signalstärke" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Justo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -16704,12 +14316,6 @@ "value" : "Favorit" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Favorito" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -16745,12 +14351,6 @@ "value" : "Knoten, die als Favorit markiert oder ignoriert wurden, bleiben immer erhalten. Knoten ohne PKC-Schlüssel werden gemäß dem festgelegten Zeitplan aus der App-Datenbank gelöscht. Knoten mit PKC-Schlüsseln werden nur gelöscht, wenn das Intervall auf 7 Tage oder länger eingestellt ist. Diese Funktion löscht nur Knoten aus der App, die nicht in der Geräteknoten-Datenbank gespeichert sind." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Los nodos favoritos e ignorados siempre se conservan. Los nodos sin claves PKC se borran de la base de datos de la aplicación según el cronograma establecido por el usuario, los nodos con claves PKC se borran solo si el intervalo se establece en 7 días o más. Esta función solo elimina los nodos de la aplicación que no están almacenados en la base de datos de nodos del dispositivo." - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -16776,12 +14376,6 @@ "value" : "Favoriten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Favoritos" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -16816,12 +14410,6 @@ "value" : "Favoriten und Knoten mit aktuellen Nachrichten werden oben in der Kontaktliste angezeigt." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Los favoritos y los nodos con mensajes recientes aparecen en la parte superior de la lista de contactos." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -16862,12 +14450,6 @@ "value" : "Letzte Position eines Knotens holen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Obtener la última posición de un nodo determinado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -16896,12 +14478,6 @@ }, "Fifteen Minutes" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "quince minutos" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -16936,12 +14512,6 @@ "value" : "Fünfzehn Sekunden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "quince segundos" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -17000,12 +14570,6 @@ }, "File Storage" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Almacenamiento de archivos" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -17035,12 +14599,6 @@ "Files Available" : { "comment" : "Data source label when files exist but none are active", "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Archivos disponibles" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -17057,12 +14615,6 @@ "value" : "Filtere die Knotenliste und die Mesh-Karte nach der Nähe zu deinem Handy." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Filtre la lista de nodos y el mapa de malla según la proximidad a su teléfono." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -17079,12 +14631,6 @@ "value" : "Kontakt suchen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "encontrar un contacto" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -17125,12 +14671,6 @@ "value" : "Einen Knoten finden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Encuentra un nodo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -17171,12 +14711,6 @@ "value" : "Beenden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Finalizar" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -17242,12 +14776,6 @@ "value" : "Ziel" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Finalizar" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -17264,12 +14792,6 @@ "value" : "Firmware" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "firmware" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -17304,12 +14826,6 @@ }, "Firmware update docs" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Documentos de actualización de firmware" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -17350,12 +14866,6 @@ "value" : "Firmwareaktualisierungen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Actualizaciones de firmware" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -17396,12 +14906,6 @@ "value" : "Firmware Version" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Versión de firmware" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -17460,12 +14964,6 @@ }, "First heard" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "escuchado por primera vez" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -17506,12 +15004,6 @@ "value" : "Fünf Stunden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "cinco horas" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -17576,12 +15068,6 @@ "value" : "Fünf Minuten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "cinco minutos" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -17616,12 +15102,6 @@ "value" : "Fünf Sekunden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "cinco segundos" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -17686,12 +15166,6 @@ "value" : "Feste PIN" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pasador fijo" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -17750,12 +15224,6 @@ }, "Fixed Position" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Posición fija" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -17784,12 +15252,6 @@ }, "Flip Screen" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Voltear pantalla" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -17818,12 +15280,6 @@ }, "Flip screen vertically" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Voltear la pantalla verticalmente" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -17858,12 +15314,6 @@ "value" : "Folgen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Seguir" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -17928,12 +15378,6 @@ "value" : "Folgen mit Steuerkurs" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Seguir con encabezado" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -17992,12 +15436,6 @@ }, "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." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Para todas las funciones de Mqtt además del informe de mapa, también debe configurar el enlace ascendente y descendente para cada canal que desee conectar a través de Mqtt." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -18038,12 +15476,6 @@ "value" : "Für alle" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Para todos" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -18078,12 +15510,6 @@ "value" : "Für mich" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Para mí" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -18118,12 +15544,6 @@ "value" : "Achtundvierzig Stunden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "cuarenta y ocho horas" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -18188,12 +15608,6 @@ "value" : "Fündundvierzig Sekunden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "cuarenta y cinco segundos" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -18258,12 +15672,6 @@ "value" : "Vier Stunden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "cuatro horas" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -18328,12 +15736,6 @@ "value" : "Vier Sekunden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "cuatro segundos" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -18398,12 +15800,6 @@ "value" : "Frequenz" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Frecuencia" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -18432,12 +15828,6 @@ }, "Frequency Override" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Anulación de frecuencia" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -18466,12 +15856,6 @@ }, "Frequency Slot" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ranura de frecuencia" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -18500,12 +15884,6 @@ }, "Friendly name" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nombre amigable" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -18540,12 +15918,6 @@ }, "Friendly name used to format message sent to mesh. Example: A name \"Motion\" would result in a message \"Motion detected\"" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nombre descriptivo utilizado para formatear el mensaje enviado a la malla. Ejemplo: un nombre \"Movimiento\" daría como resultado un mensaje \"Movimiento detectado\"." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -18580,12 +15952,6 @@ }, "From Radio (RX): %lld" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Desde radio (RX): %lld" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -18596,12 +15962,6 @@ }, "Full Support" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Soporte completo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -18636,12 +15996,6 @@ }, "Generate a new private key to replace the one currently in use. The public key will automatically be regenerated from your private key." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Genere una nueva clave privada para reemplazar la que está actualmente en uso. La clave pública se regenerará automáticamente a partir de su clave privada." - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -18664,12 +16018,6 @@ "value" : "QR Code Erzeugen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Generar código QR" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -18728,12 +16076,6 @@ }, "Get custom waterproof solar and detection sensor router nodes, aluminium desktop nodes and rugged handsets." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Obtenga nodos de enrutador de sensores de detección y solares impermeables personalizados, nodos de escritorio de aluminio y teléfonos resistentes." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -18768,12 +16110,6 @@ "value" : "Knotenposition ermitteln" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Obtener la posición del nodo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -18808,12 +16144,6 @@ }, "Get NRF DFU from the App Store" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Obtenga NRF DFU en la App Store" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -18854,12 +16184,6 @@ "value" : "Los geht's" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "empezar" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -18870,12 +16194,6 @@ }, "Get the latest stable firmware" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Obtenga el firmware estable más reciente" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -18910,12 +16228,6 @@ }, "GitHub Repository" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Repositorio GitHub" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -18932,12 +16244,6 @@ }, "Good" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bien" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -18966,12 +16272,6 @@ }, "GPIO" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "GPIO" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -19006,12 +16306,6 @@ }, "GPIO Output Duration" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Duración de la salida GPIO" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -19040,12 +16334,6 @@ }, "GPIO pin for rotary encoder A port." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pin GPIO para el puerto A del codificador rotatorio." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -19074,12 +16362,6 @@ }, "GPIO pin for rotary encoder B port." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pin GPIO para el puerto B del codificador rotatorio." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -19108,12 +16390,6 @@ }, "GPIO pin for rotary encoder Press port." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pin GPIO para codificador rotatorio Puerto de prensa." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -19142,12 +16418,6 @@ }, "GPIO Pin to monitor" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pin GPIO para monitorear" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -19176,12 +16446,6 @@ }, "GPS EN GPIO" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "GPS EN GPIO" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -19216,12 +16480,6 @@ }, "GPS Receive GPIO" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Recepción GPS GPIO" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -19256,12 +16514,6 @@ }, "GPS Transmit GPIO" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Transmisión GPS GPIO" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -19302,12 +16554,6 @@ "value" : "Gruppennachricht" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mensaje grupal" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -19342,12 +16588,6 @@ }, "Gusts %@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ráfagas %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -19376,12 +16616,6 @@ }, "HaHa" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ja ja" - } - }, "he" : { "stringUnit" : { "state" : "translated", @@ -19422,12 +16656,6 @@ }, "Hard Reset" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Restablecimiento completo" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -19438,12 +16666,6 @@ }, "Hardware" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Hardware" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -19478,12 +16700,6 @@ }, "Hazardous" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Peligroso" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -19512,12 +16728,6 @@ }, "Heading" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Título" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -19552,12 +16762,6 @@ "value" : "Kurs: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Título: %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -19592,12 +16796,6 @@ "value" : "Gehört" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Escuchó" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -19662,12 +16860,6 @@ "value" : "Herz" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Corazón" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -19726,12 +16918,6 @@ }, "Hide alerts" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ocultar alertas" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -19766,12 +16952,6 @@ }, "Hide Alerts" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ocultar alertas" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -19806,12 +16986,6 @@ "value" : "HOCH" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "ALTO" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -19852,12 +17026,6 @@ "value" : "Wandern" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Senderismo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -19892,12 +17060,6 @@ }, "History Return Max" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Historial Retorno Max" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -19926,12 +17088,6 @@ }, "History Return Window" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ventana de retorno del historial" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -19966,12 +17122,6 @@ "value" : "Hops Entfernt" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "salta lejos" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -20006,12 +17156,6 @@ "value" : "Hops Entfernt %d" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Salta lejos %d" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -20046,12 +17190,6 @@ "value" : "Hops Entfernt:" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Saltos lejos:" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -20086,12 +17224,6 @@ "value" : "Hops Entfernt: %d" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Saltos lejos: %d" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -20126,12 +17258,6 @@ "value" : "Stunde" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Hora" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -20166,12 +17292,6 @@ }, "Hourly Duty Cycle" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ciclo de trabajo por hora" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -20200,12 +17320,6 @@ }, "How long the screen remains on after the user button is pressed or messages are received." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cuánto tiempo permanece encendida la pantalla después de presionar el botón de usuario o recibir mensajes." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -20240,12 +17354,6 @@ }, "How often device metrics are sent out over the mesh. Default is 30 minutes." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Con qué frecuencia se envían las métricas del dispositivo a través de la malla. El valor predeterminado es 30 minutos." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -20280,12 +17388,6 @@ }, "How often environment metrics are sent out over the mesh. Default is 30 minutes." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Con qué frecuencia se envían métricas ambientales a través de la malla. El valor predeterminado es 30 minutos." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -20320,12 +17422,6 @@ }, "How often power metrics are sent out over the mesh. Default is 30 minutes." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Con qué frecuencia se envían métricas de potencia a través de la malla. El valor predeterminado es 30 minutos." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -20360,12 +17456,6 @@ }, "How often should we try to get a GPS position." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "¿Con qué frecuencia debemos intentar obtener una posición GPS?" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -20400,12 +17490,6 @@ }, "How often to send detection sensor state to mesh regardless of detection. Default is Never." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Con qué frecuencia enviar el estado del sensor de detección a la malla independientemente de la detección. El valor predeterminado es Nunca." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -20446,12 +17530,6 @@ "value" : "How often we can send a message to the mesh when people are detected." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Con qué frecuencia podemos enviar un mensaje a la malla cuando se detectan personas." - } - }, "he" : { "stringUnit" : { "state" : "translated", @@ -20510,12 +17588,6 @@ "value" : "Wie wird die Firmware aktualisiert" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cómo actualizar el firmware" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -20550,12 +17622,6 @@ }, "Hum" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tararear" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -20590,12 +17656,6 @@ "value" : "Luftfeuchtigkeit" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Humedad" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -20624,12 +17684,6 @@ }, "Hybrid" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Híbrido" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -20688,12 +17742,6 @@ }, "Hybrid Flyover" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Paso elevado híbrido" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -20752,12 +17800,6 @@ }, "I have read and understand the above. I voluntarily consent to the unencrypted transmission of my node data via MQTT." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "He leído y entiendo lo anterior. Doy mi consentimiento voluntariamente para la transmisión sin cifrar de los datos de mi nodo a través de MQTT." - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -20780,12 +17822,6 @@ }, "IAQ" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "IAQ" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -20820,12 +17856,6 @@ }, "IAQ " : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "IAQ" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -20860,12 +17890,6 @@ }, "IAQ %lld" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "IAQ %lld" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -20906,12 +17930,6 @@ "value" : "Emoji" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Icono" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -20946,12 +17964,6 @@ }, "Icons" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Iconos" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -20962,12 +17974,6 @@ }, "If DOP is set, use HDOP / VDOP values instead of PDOP" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Si se configura DOP, use valores HDOP/VDOP en lugar de PDOP" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -21002,12 +18008,6 @@ }, "If enabled, the 'output' Pin will be pulled active high, disabled means active low." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Si está habilitado, el pin de 'salida' se activará alto, deshabilitado significa activo bajo." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -21042,12 +18042,6 @@ }, "If it is hard to access your device's reset button enter DFU mode here." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Si le resulta difícil acceder al botón de reinicio de su dispositivo, ingrese al modo DFU aquí." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -21082,12 +18076,6 @@ }, "If set, any packets you send will be echoed back to your device." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Si está configurado, cualquier paquete que envíe se enviará a su dispositivo." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -21122,12 +18110,6 @@ }, "If the default region topic is too busy you can choose a more local topic." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Si el tema de la región predeterminada está demasiado ocupado, puede elegir un tema más local." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -21162,12 +18144,6 @@ }, "Ignore MQTT" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ignorar MQTT" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -21202,12 +18178,6 @@ }, "Ignore Node" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ignorar nodo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -21242,12 +18212,6 @@ }, "Ignored" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "ignorado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -21282,12 +18246,6 @@ }, "Ignores observed messages from foreign meshes like Local Only, but takes it step further by also ignoring messages from nodes not already in the node's known list." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ignora los mensajes observados de mallas externas como Solo local, pero va un paso más allá al ignorar también los mensajes de nodos que aún no están en la lista conocida del nodo." - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -21310,12 +18268,6 @@ }, "Ignores observed messages from foreign meshes that are open or those which it cannot decrypt. Only rebroadcasts message on the nodes local primary / secondary channels." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ignora los mensajes observados de mallas externas que están abiertas o aquellas que no puede descifrar. Sólo retransmite mensajes en los canales primarios/secundarios locales del nodo." - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -21344,12 +18296,6 @@ "value" : "Route importieren" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ruta de importación" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -21384,12 +18330,6 @@ }, "In addition to Config, Keys and BLE bonds will be wiped" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Además de la configuración, se borrarán las claves y los enlaces BLE." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -21406,12 +18346,6 @@ "value" : "Include" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Incluir" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -21476,12 +18410,6 @@ "value" : "Eingehende Nachrichten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mensajes entrantes" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -21498,12 +18426,6 @@ "value" : "Unvollständig" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Incompleto" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -21538,12 +18460,6 @@ }, "India" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "India" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -21566,12 +18482,6 @@ }, "Indoor Air Quality" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Calidad del aire interior" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -21606,12 +18516,6 @@ }, "Indoor Air Quality (IAQ)" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Calidad del aire interior (IAQ)" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -21652,12 +18556,6 @@ "value" : "Router - Mesh Pakete werden bevorzugt über diesen Knoten gerouted. Dieser Knoten wird nicht von einer Client App benutzt. WLAN, Bluetooth und Display sind aus." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nodo de infraestructura únicamente en una torre o cima de una montaña. No debe usarse para techos o nodos móviles. Necesita una cobertura excepcional. Visible en la lista de nodos." - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -21716,12 +18614,6 @@ }, "Inputs" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Entradas" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -21762,12 +18654,6 @@ "value" : "Ungültiger Dateiinhalt. Bitte überprüfe das Dateiformat." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Contenido del archivo no válido. Por favor verifique el formato del archivo." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -21778,12 +18664,6 @@ }, "Inverted top bar for 2 Color display" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Barra superior invertida para pantalla de 2 colores" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -21818,12 +18698,6 @@ }, "Japan" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Japón" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -21852,12 +18726,6 @@ }, "JSON Enabled" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "JSON habilitado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -21892,12 +18760,6 @@ }, "JSON mode is a limited, unencrypted MQTT output for locally integrating with home assistant" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "El modo JSON es una salida MQTT limitada y sin cifrar para la integración local con el asistente doméstico" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -21932,12 +18794,6 @@ }, "Jump to present" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Saltar al presente" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -21966,12 +18822,6 @@ "value" : "Schlüssel" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Llave" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -22006,12 +18856,6 @@ }, "Key Backup" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Copia de seguridad clave" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -22028,12 +18872,6 @@ }, "Key Mapping" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mapeo de claves" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -22068,12 +18906,6 @@ "value" : "Schlüsselgröße" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tamaño de clave" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -22102,12 +18934,6 @@ }, "Korea" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Corea" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -22142,12 +18968,6 @@ "value" : "Zuletzt gehört" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Escuchado por última vez" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -22195,12 +19015,6 @@ "value" : "Breitengrad" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Latitud" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -22235,12 +19049,6 @@ }, "Latitude in degrees (e.g., 37.7749)" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Latitud en grados (por ejemplo, 37,7749)" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -22257,12 +19065,6 @@ }, "Latitude must be between -90 and 90 degrees" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "La latitud debe estar entre -90 y 90 grados." - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -22279,12 +19081,6 @@ }, "LED Heartbeat" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Latido del corazón LED" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -22325,12 +19121,6 @@ "value" : "LED Status" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Estado del LED" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -22371,12 +19161,6 @@ "value" : "Links" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Izquierda" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -22441,12 +19225,6 @@ "value" : "Level" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nivel" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -22505,12 +19283,6 @@ }, "Licensed Operator" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Operador Licenciado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -22545,12 +19317,6 @@ }, "Limit all periodic broadcast intervals especially telemetry and position. If you need to increase hops, do it on nodes at the edges, not the ones in the middle. MQTT is not advised when you are duty cycle restricted because the gateway node is then doing all the work." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Limite todos los intervalos de transmisión periódica, especialmente la telemetría y la posición. Si necesita aumentar los saltos, hágalo en los nodos de los bordes, no en los del medio. No se recomienda MQTT cuando el ciclo de trabajo está restringido porque el nodo de puerta de enlace está haciendo todo el trabajo." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -22585,12 +19351,6 @@ }, "Line Series" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Serie de línea" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -22619,12 +19379,6 @@ }, "Loading Logs. . ." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cargando registros. . ." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -22661,12 +19415,6 @@ "comment" : "A label displayed above the options for local network access.", "isCommentAutoGenerated" : true, "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Acceso a la red local" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -22683,12 +19431,6 @@ "value" : "Standort:" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ubicación:" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -22729,12 +19471,6 @@ "value" : "Gesperrt" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "bloqueado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -22769,12 +19505,6 @@ }, "Log Levels" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Niveles de registro" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -22815,12 +19545,6 @@ "value" : "Logging" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Explotación florestal" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -22879,12 +19603,6 @@ }, "Logs" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Registros" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -22919,12 +19637,6 @@ }, "Logs:" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Registros:" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -22965,12 +19677,6 @@ "value" : "Langer Name" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nombre largo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -23011,12 +19717,6 @@ "value" : "Durch langes Gedrückthalten kannst du den Kontakt zu deinen Favoriten hinzufügen, stumm schalten oder eine Unterhaltung löschen." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mantenga presionado para marcar como favorito, silenciar el contacto o eliminar una conversación." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -23051,12 +19751,6 @@ }, "Long Range - Fast" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Largo alcance - Rápido" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -23085,12 +19779,6 @@ }, "Long Range - Moderate" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Largo alcance - Moderado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -23119,12 +19807,6 @@ }, "Long Range - Slow" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Largo alcance - Lento" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -23159,12 +19841,6 @@ "value" : "Längengrad" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Longitud" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -23199,12 +19875,6 @@ }, "Longitude in degrees (e.g., -122.4194)" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Longitud en grados (p. ej., -122,4194)" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -23221,12 +19891,6 @@ }, "Longitude must be between -180 and 180 degrees" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "La longitud debe estar entre -180 y 180 grados." - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -23249,12 +19913,6 @@ "value" : "LoRa" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "lora" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -23319,12 +19977,6 @@ "value" : "LoRa Einstellungen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración LoRa" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -23383,12 +20035,6 @@ }, "LoRa Config Changes:" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cambios en la configuración de LoRa:" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -23405,12 +20051,6 @@ "value" : "LoRa config empfangen: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración de LoRa recibida: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -23475,12 +20115,6 @@ "value" : "Tracker" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Objetos perdidos y encontrados" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -23515,12 +20149,6 @@ }, "LOW" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "BAJO" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -23555,12 +20183,6 @@ "value" : "Niedriger Akkustand" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Batería baja" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -23577,12 +20199,6 @@ "value" : "M5 Stack Card KB / RAK Tastenfeld" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tarjeta de pila M5 Teclado KB / RAK" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -23623,12 +20239,6 @@ }, "Malaysia 433MHz" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Malasia 433MHz" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -23657,12 +20267,6 @@ }, "Malaysia 919MHz" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Malasia 919MHz" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -23697,12 +20301,6 @@ "value" : "Kanäle verwalten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Administrar canales" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -23762,12 +20360,6 @@ "Manage custom map overlays" : { "comment" : "Subtitle for map data management", "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Administrar superposiciones de mapas personalizados" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -23809,12 +20401,6 @@ "value" : "Kartendaten verwalten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Administrar datos de mapas" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -23825,12 +20411,6 @@ }, "Managed Device" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dispositivo administrado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -23865,12 +20445,6 @@ }, "Manual" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Manual" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -23893,12 +20467,6 @@ "value" : "Manuelle Konfiguration" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración manual" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -23957,12 +20525,6 @@ }, "Manual connection string" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cadena de conexión manual" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -23976,12 +20538,6 @@ }, "Map Data" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Datos del mapa" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -23998,12 +20554,6 @@ "value" : "Kartenoptionen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Opciones de mapa" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -24044,12 +20594,6 @@ "value" : "Karten-Overlays" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Superposiciones de mapas" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -24108,12 +20652,6 @@ }, "Map Publish Interval" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Intervalo de publicación de mapas" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -24148,12 +20686,6 @@ }, "Map Report" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Informe de mapa" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -24194,12 +20726,6 @@ "value" : "Maximale Wiederholungen erreicht" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Retransmisión máxima alcanzada" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -24258,12 +20784,6 @@ }, "Medium Range - Fast" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rango medio - Rápido" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -24292,12 +20812,6 @@ }, "Medium Range - Slow" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rango medio - Lento" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -24326,12 +20840,6 @@ }, "Mesh activity update" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Actualización de actividad de malla" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -24366,12 +20874,6 @@ "value" : "Mesh Live Aktivität" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Actividad en vivo de malla" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -24436,12 +20938,6 @@ "value" : "Mesh Karte" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mapa de malla" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -24506,12 +21002,6 @@ "value" : "Standort auf der Mesh-Karte" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ubicación del mapa de malla" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -24522,12 +21012,6 @@ }, "Meshtastic" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Meshtástico" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -24547,12 +21031,6 @@ "value" : "Meshtastic Knoten %@ hat Kanäle mit dir geteilt" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Meshtastic Node %@ ha compartido canales contigo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -24587,12 +21065,6 @@ "value" : "Meshtastic verwendet den Standort deines Handys, um eine Reihe von Funktionen zu ermöglichen. Du kannst deine Standortberechtigungen jederzeit in den Einstellungen aktualisieren." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Meshtastic utiliza la ubicación de su teléfono para habilitar una serie de funciones. Puede actualizar sus permisos de ubicación en cualquier momento desde la configuración." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -24603,12 +21075,6 @@ }, "Meshtastic® Copyright Meshtastic LLC" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Meshtastic® Copyright Meshtastic LLC" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -24643,12 +21109,6 @@ "value" : "Nachricht" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mensaje" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -24683,12 +21143,6 @@ "value" : "Nachrichteninhalt überschreitet 200 Bytes." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "El contenido del mensaje supera los 200 bytes." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -24723,12 +21177,6 @@ "value" : "Nachrichtendetails" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Detalles del mensaje" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -24793,12 +21241,6 @@ "value" : "Nachricht von der Textnachricht-App empfangen." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mensaje recibido de la aplicación de mensajes de texto." - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -24858,12 +21300,6 @@ "Message Size" : { "comment" : "VoiceOver label for message size", "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tamaño del mensaje" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -24880,12 +21316,6 @@ }, "Message Status Options" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Opciones de estado del mensaje" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -24920,12 +21350,6 @@ "value" : "Nachrichten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mensajes" - } - }, "he" : { "stringUnit" : { "state" : "translated", @@ -24984,12 +21408,6 @@ "value" : "Nachrichten getrennt mit |" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Los mensajes se separan con |" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -25018,12 +21436,6 @@ }, "Messaging" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mensajería" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -25040,12 +21452,6 @@ }, "Metric" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Métrico" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -25080,12 +21486,6 @@ "value" : "Mittag" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Uno" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -25120,12 +21520,6 @@ "value" : "Minimum Distanz" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Distancia mínima" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -25160,12 +21554,6 @@ "value" : "Minimum Intervall" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Intervalo mínimo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -25194,12 +21582,6 @@ }, "Minimum time between detection broadcasts" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tiempo mínimo entre transmisiones de detección" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -25228,12 +21610,6 @@ }, "Mininum time between detection broadcasts. Default is 45 seconds." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tiempo mínimo entre transmisiones de detección. El valor predeterminado es 45 segundos." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -25268,12 +21644,6 @@ "value" : "Modus" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Modo" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -25332,12 +21702,6 @@ }, "Model" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Modelo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -25372,12 +21736,6 @@ }, "Moderate" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Moderado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -25418,12 +21776,6 @@ "value" : "Modul Konfiguration" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración del módulo" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -25488,12 +21840,6 @@ "value" : "Morgen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mañana" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -25528,12 +21874,6 @@ "value" : "Die meisten Daten in deinem Mesh werden über den primären Kanal gesendet. Du kannst sekundäre Kanäle einrichten, um zusätzliche Nachrichtengruppen zu erstellen, die durch ihren eigenen Schlüssel gesichert sind. [Tipps zur Kanalkonfiguration](https://meshtastic.org/docs/configuration/radio/channels/)" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "La mayoría de los datos de su malla se envían a través del canal principal. Puede configurar canales secundarios para crear grupos de mensajería adicionales protegidos por su propia clave. [Consejos de configuración de canales](https://meshtastic.org/docs/configuration/tips/)" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -25592,12 +21932,6 @@ }, "MQTT" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "MQTT" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -25638,12 +21972,6 @@ "value" : "MQTT Client Proxy" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Proxy de cliente MQTT" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -25708,12 +22036,6 @@ "value" : "MQTT Konfiguration" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración MQTT" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -25778,12 +22100,6 @@ "value" : "MQTT Modulkonfiguration empfangen: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración del módulo MQTT recibida: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -25848,12 +22164,6 @@ "value" : "Multiplier" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Multiplicador" - } - }, "he" : { "stringUnit" : { "state" : "translated", @@ -25900,12 +22210,6 @@ }, "Must be a single emoji" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Debe ser un solo emoji" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -25940,12 +22244,6 @@ "value" : "MyInfo empfangen: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mi información recibida: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -26005,12 +22303,6 @@ "Nag timeout" : { "extractionState" : "stale", "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Se acabó el tiempo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -26038,14 +22330,7 @@ } }, "Nag Timeout" : { - "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tiempo agotado" - } - } - } + }, "Name" : { "localizations" : { @@ -26055,12 +22340,6 @@ "value" : "Name" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nombre" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -26101,12 +22380,6 @@ "value" : "Name muss kürzer als 30 Bytes sein" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "El nombre debe tener menos de 30 bytes." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -26141,12 +22414,6 @@ }, "Navigate to node" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Navegar al nodo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -26175,12 +22442,6 @@ }, "Nearby Topics" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Temas cercanos" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -26215,12 +22476,6 @@ "value" : "Netzwerk" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Red" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -26285,12 +22540,6 @@ "value" : "Netzwerkeinstellungen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración de red" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -26355,12 +22604,6 @@ "value" : "Netzwerkkonfiguration empfangen: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración de red recibida: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -26419,12 +22662,6 @@ }, "Network Status Orange" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Estado de la red naranja" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -26459,12 +22696,6 @@ }, "Network Status Red" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Estado de la red Rojo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -26499,12 +22730,6 @@ }, "New Node" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nuevo nodo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -26533,12 +22758,6 @@ }, "New Node has been discovered" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Se ha descubierto un nuevo nodo." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -26573,12 +22792,6 @@ "value" : "Neue Knoten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nuevos nodos" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -26589,12 +22802,6 @@ }, "New Zealand 865MHz" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nueva Zelanda 865MHz" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -26629,12 +22836,6 @@ "value" : "Neuere Firmware ist verfügbar" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Hay un firmware más nuevo disponible" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -26675,12 +22876,6 @@ "value" : "Nacht" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Noche" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -26715,12 +22910,6 @@ "value" : "NMEA Positionen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Posiciones NMEA" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -26785,12 +22974,6 @@ "value" : "Kein Kanal" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sin canal" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -26855,12 +23038,6 @@ "value" : "Kein verbundener Knoten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ningún nodo conectado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -26896,12 +23073,6 @@ "value" : "Keine Daten vorhanden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sin datos" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -26942,12 +23113,6 @@ "value" : "Kein Gerät verbunden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ningún dispositivo conectado" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -27006,12 +23171,6 @@ }, "No Device Metrics" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sin métricas de dispositivo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -27040,12 +23199,6 @@ }, "No Environment Metrics" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sin métricas ambientales" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -27086,12 +23239,6 @@ "value" : "Keine Dateien hochgeladen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "No se subieron archivos" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -27108,12 +23255,6 @@ "value" : "Keine Schnittstelle" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sin interfaz" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -27173,12 +23314,6 @@ "No map data files uploaded" : { "comment" : "Message when no files are uploaded", "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "No se han subido archivos de datos de mapas" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -27189,12 +23324,6 @@ }, "No PAX Counter Logs" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sin registros de contador de PAX" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -27235,12 +23364,6 @@ "value" : "Keine PIN (geht einfach)" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sin PIN (simplemente funciona)" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -27305,12 +23428,6 @@ "value" : "Keine Positionen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sin posiciones" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -27339,12 +23456,6 @@ }, "No Power Metrics" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sin métricas de energía" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -27379,12 +23490,6 @@ "value" : "Keine Antwort" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sin respuesta" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -27449,12 +23554,6 @@ "value" : "Keine Route" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sin ruta" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -27519,12 +23618,6 @@ "value" : "Knoten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nodo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -27559,12 +23652,6 @@ "value" : "Node Core Data Backup %1$@/%2$@ - %3$@ - %4$@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Copia de seguridad de datos principales del nodo %@/%@ - %@ - %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -27605,12 +23692,6 @@ "value" : "Knoten hat keine Position" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "El nodo no tiene posiciones" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -27645,12 +23726,6 @@ "value" : "Knoten Historie" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Historia del nodo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -27679,12 +23754,6 @@ }, "Node Info Broadcast Interval" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Intervalo de transmisión de información de nodo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -27719,12 +23788,6 @@ "value" : "Knotenkarte" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mapa de nodos" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -27763,12 +23826,6 @@ "value" : "Knotennummer" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Número de nodo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -27809,12 +23866,6 @@ "value" : "Knoten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nodos" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -27873,12 +23924,6 @@ "value" : "Knoten (%@)" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nodos (%@)" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -27943,12 +23988,6 @@ "value" : "Keins" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ninguno" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -28007,12 +24046,6 @@ }, "Not a valid route file" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "No es un archivo de ruta válido" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -28047,12 +24080,6 @@ "value" : "Nicht authorisiert" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "No autorizado" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -28109,24 +24136,8 @@ } } }, - "Not Connected" : { - "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "No conectado" - } - } - } - }, "Not Present" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "No presente" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -28185,12 +24196,6 @@ "value" : "Knoten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Notas" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -28225,12 +24230,6 @@ "value" : "Mitteilungen für Kanal- und Direktnachrichten." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Notificaciones por canal y mensajes directos." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -28247,12 +24246,6 @@ "value" : "Mitteilungen bei niedrigem Akkustand des verbundenen Funkgeräts." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Notificaciones de alertas de batería baja para el dispositivo conectado." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -28269,12 +24262,6 @@ "value" : "Mitteilungen für neu entdeckte Knoten." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Notificaciones para nodos recién descubiertos." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -28291,12 +24278,6 @@ "value" : "Anzahl Hops" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Número de saltos" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -28331,12 +24312,6 @@ "value" : "Anzahl Einträge" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Número de registros" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -28371,12 +24346,6 @@ "value" : "Anzahl Satelliten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Número de satélites" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -28405,12 +24374,6 @@ }, "Ok" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "De acuerdo" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -28427,12 +24390,6 @@ "value" : "Ok" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "DE ACUERDO" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -28461,12 +24418,6 @@ }, "Ok to MQTT" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ok para MQTT" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -28507,12 +24458,6 @@ "value" : "OLED Typ" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tipo OLED" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -28553,12 +24498,6 @@ "value" : "Nur beim Starten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sólo en el arranque" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -28617,12 +24556,6 @@ }, "Onboarding for licensed operators requires firmware 2.0.20 or greater. Make sure to refer to your local regulations and contact the local amateur frequency coordinators with questions." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "La incorporación de operadores con licencia requiere firmware 2.0.20 o superior. Asegúrese de consultar las regulaciones locales y comuníquese con los coordinadores locales de frecuencias de aficionados si tiene preguntas." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -28663,12 +24596,6 @@ "value" : "Eine Stunde" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Una hora" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -28733,12 +24660,6 @@ "value" : "Eine Minute" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "un minuto" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -28803,12 +24724,6 @@ "value" : "Eine Sekunde" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "un segundo" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -28873,12 +24788,6 @@ "value" : "Online" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "En línea" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -28907,12 +24816,6 @@ }, "Only permitted for SENSOR, TRACKER and TAK_TRACKER roles, this will inhibit all rebroadcasts, not unlike CLIENT_MUTE role." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Solo permitido para los roles SENSOR, TRACKER y TAK_TRACKER, esto inhibirá todas las retransmisiones, al igual que el rol CLIENT_MUTE." - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -28935,12 +24838,6 @@ }, "Only rebroadcasts packets from the core portnums: NodeInfo, Text, Position, Telemetry, and Routing." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Solo retransmite paquetes desde los portnums principales: NodeInfo, Texto, Posición, Telemetría y Enrutamiento." - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -28972,12 +24869,6 @@ "value" : "Einstellungen öffnen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Abrir configuración" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -29012,12 +24903,6 @@ }, "Optimized for 2 color displays" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Optimizado para pantallas de 2 colores" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -29052,12 +24937,6 @@ "value" : "Optimiert für ATAK-Systemkommunikation, verringert die Anzahl der Routineübertragungen." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Optimizado para la comunicación del sistema ATAK, reduce las transmisiones de rutina." - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -29116,12 +24995,6 @@ }, "Optional fields to include when assembling position messages. the more fields are included, the larger the message will be - leading to longer airtime and a higher risk of packet loss" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Campos opcionales para incluir al ensamblar mensajes de posición. Cuantos más campos se incluyan, más grande será el mensaje, lo que llevará a un mayor tiempo de emisión y a un mayor riesgo de pérdida de paquetes." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -29156,12 +25029,6 @@ }, "Optional GPIO" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "GPIO opcional" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -29196,12 +25063,6 @@ "value" : "Optionen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Opciones" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -29260,12 +25121,6 @@ }, "OS Log Entry Details" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Detalles de entrada de registro del sistema operativo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -29294,12 +25149,6 @@ }, "OTA Updates are not supported on this NRF Device." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Las actualizaciones OTA no son compatibles con este dispositivo NRF." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -29334,12 +25183,6 @@ }, "OTA Updates are not supported on your platform." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Las actualizaciones OTA no son compatibles con su plataforma." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -29374,12 +25217,6 @@ }, "Other data sources" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Otras fuentes de datos" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -29414,12 +25251,6 @@ "value" : "Ausgabe von Echtzeit-Fehlersuchprotokollen über die serielle Schnittstelle, Anzeige und Export von positionskorrigierten Geräteprotokollen über Bluetooth." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Genere registros de depuración en vivo a través de serie, vea y exporte registros de dispositivos redactados en posición a través de Bluetooth." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -29448,12 +25279,6 @@ }, "Output pin buzzer GPIO " : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Zumbador de pin de salida GPIO" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -29482,12 +25307,6 @@ }, "Output pin GPIO" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pin de salida GPIO" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -29516,12 +25335,6 @@ }, "Output pin vibra GPIO" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pin de salida vibración GPIO" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -29550,12 +25363,6 @@ }, "Overlanding" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Por tierra" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -29590,12 +25397,6 @@ }, "Override automatic OLED screen detection." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Anule la detección automática de pantalla OLED." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -29624,12 +25425,6 @@ }, "Override default screen layout." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Anular el diseño de pantalla predeterminado." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -29640,12 +25435,6 @@ }, "Packet Count" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Recuento de paquetes" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -29662,12 +25451,6 @@ "value" : "Pairing Modus" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Modo de emparejamiento" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -29732,12 +25515,6 @@ "value" : "Passwort" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Contraseña" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -29802,12 +25579,6 @@ "value" : "Pause" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pausa" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -29866,12 +25637,6 @@ }, "PAX Counter" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Contador de pasajeros" - } - }, "he" : { "stringUnit" : { "state" : "translated", @@ -29924,12 +25689,6 @@ }, "PAX Counter Config" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración del contador PAX" - } - }, "he" : { "stringUnit" : { "state" : "translated", @@ -29976,12 +25735,6 @@ }, "PAX Counter config received: %@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración del contador PAX recibida: %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -30016,12 +25769,6 @@ }, "PAX Counter Log" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Registro de contador de PAX" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -30062,12 +25809,6 @@ "value" : "PAX Counter message received for: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mensaje del contador de PAX recibido de: %@" - } - }, "he" : { "stringUnit" : { "state" : "translated", @@ -30120,12 +25861,6 @@ }, "paxcounter.log" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "paxcounter.log" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -30154,12 +25889,6 @@ "value" : "Verbundenen Knoten auf Werkseinstellungen zurücksetzen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Realice un restablecimiento de fábrica en el nodo al que está conectado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -30188,12 +25917,6 @@ }, "Philippines 433MHz" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Filipinas 433MHz" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -30222,12 +25945,6 @@ }, "Philippines 868MHz" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Filipinas 868MHz" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -30256,12 +25973,6 @@ }, "Philippines 915MHz" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Filipinas 915MHz" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -30296,12 +26007,6 @@ "value" : "Telefon GPS" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "GPS del teléfono" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -30366,12 +26071,6 @@ "value" : "Standorteinstellungen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ubicación del teléfono" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -30382,12 +26081,6 @@ }, "Pin %lld" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Fijar %lld" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -30416,12 +26109,6 @@ }, "Pin A" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pin A" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -30450,12 +26137,6 @@ }, "Pin B" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pin B" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -30490,12 +26171,6 @@ "value" : "PKI-basierte Knotenadministration, benötigt Firmware Version 2.5+" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Administración de nodos basada en PKI, requiere versión de firmware 2.5+" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -30530,12 +26205,6 @@ }, "Please be advised that because the map report is not encrypted, your data may be stored and displayed permanently by third parties. Meshtastic does not assume responsibility for any such storage, display or disclosure of this data." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tenga en cuenta que debido a que el informe del mapa no está cifrado, terceros pueden almacenar y mostrar sus datos de forma permanente. Meshtastic no asume responsabilidad por dicho almacenamiento, exhibición o divulgación de estos datos." - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -30564,12 +26233,6 @@ "value" : "Bitte verbinde dich mit einem Funkgerät, um die Einstellungen zu ändern." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Conéctese a una radio para configurar los ajustes." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -30610,12 +26273,6 @@ "value" : "Bitte lege eine Region fest" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Por favor establece una región" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -30644,12 +26301,6 @@ }, "Points of Interest" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Puntos de interés" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -30684,12 +26335,6 @@ "value" : "Kacke" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Caca" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -30748,12 +26393,6 @@ }, "Position" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Posición" - } - }, "he" : { "stringUnit" : { "state" : "translated", @@ -30806,12 +26445,6 @@ "value" : "Positionseinstellungen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración de posición" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -30876,12 +26509,6 @@ "value" : "Positionskonfiguration empfangen: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración de posición recibida: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -30934,12 +26561,6 @@ }, "Position Exchange Failed" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Error en el intercambio de posición" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -30968,12 +26589,6 @@ }, "Position Exchange Requested" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Intercambio de posición solicitado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -31002,12 +26617,6 @@ }, "Position Flags" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Banderas de posición" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -31036,12 +26645,6 @@ }, "Position Log" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Registro de posición" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -31076,12 +26679,6 @@ }, "Position Log %lld Points" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Registro de posición %lld puntos" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -31110,12 +26707,6 @@ }, "Position Packet" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Paquete de posición" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -31150,12 +26741,6 @@ "value" : "Position gesendet" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Posición enviada" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -31184,12 +26769,6 @@ }, "Positions Enabled" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Posiciones Habilitadas" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -31224,12 +26803,6 @@ }, "Positions will be provided by your device GPS, if you select disabled or not present you can set a fixed position." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Las posiciones serán proporcionadas por el GPS de su dispositivo; si selecciona deshabilitado o no presente, puede establecer una posición fija." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -31270,12 +26843,6 @@ "value" : "Strom" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Fuerza" - } - }, "he" : { "stringUnit" : { "state" : "translated", @@ -31334,12 +26901,6 @@ "value" : "Stromkonfiguration" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración de energía" - } - }, "he" : { "stringUnit" : { "state" : "translated", @@ -31392,12 +26953,6 @@ }, "Power config received: %@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración de energía recibida: %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -31432,12 +26987,6 @@ }, "Power Metrics" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Métricas de energía" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -31466,12 +27015,6 @@ }, "Power Metrics Log" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Registro de métricas de energía" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -31506,12 +27049,6 @@ }, "Power Off" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Apagar" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -31541,12 +27078,6 @@ "Power Options" : { "extractionState" : "stale", "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Opciones de energía" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -31581,12 +27112,6 @@ "value" : "Stromsparen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ahorro de energía" - } - }, "he" : { "stringUnit" : { "state" : "translated", @@ -31639,12 +27164,6 @@ }, "Power Screen" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pantalla de energía" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -31672,14 +27191,7 @@ } }, "Power Sensor Options" : { - "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Opciones de sensores de potencia" - } - } - } + }, "Powered" : { "localizations" : { @@ -31689,12 +27201,6 @@ "value" : "Angeschaltet" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Motorizado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -31729,12 +27235,6 @@ "value" : "Genaue Position" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ubicación precisa" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -31769,12 +27269,6 @@ "value" : "Voreinstellungen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Preajustes" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -31809,12 +27303,6 @@ }, "Press Pin" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pin de prensa" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -31843,12 +27331,6 @@ }, "Pressure" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Presión" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -31883,12 +27365,6 @@ "value" : "Primär" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Primario" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -31953,12 +27429,6 @@ "value" : "Erster Admin-Schlüssel" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Clave de administrador principal" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -31993,12 +27463,6 @@ }, "Primary GPIO" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "GPIO primario" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -32033,12 +27497,6 @@ "value" : "Privater Schlüssel" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Clave privada" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -32079,12 +27537,6 @@ "value" : "Prozess" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Proceso" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -32149,12 +27601,6 @@ "value" : "Datei wird verarbeitet…" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Procesando archivo..." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -32195,12 +27641,6 @@ "value" : "Projektinformationen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Información del proyecto" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -32241,12 +27681,6 @@ "value" : "Protobufs" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Protobufs" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -32311,12 +27745,6 @@ "value" : "Teile anonyme Nutzungsstatistiken und Absturzberichte." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Proporcione estadísticas de uso anónimas e informes de fallos." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -32327,12 +27755,6 @@ }, "Provide Confirmation" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Proporcionar confirmación" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -32349,12 +27771,6 @@ "value" : "Öffentlicher Schlüssel" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Clave pública" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -32389,12 +27805,6 @@ }, "Public Key Encryption" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cifrado de clave pública" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -32429,12 +27839,6 @@ }, "Public Key Mismatch" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "La clave pública no coincide" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -32469,12 +27873,6 @@ }, "PWD" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "PCD" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -32509,12 +27907,6 @@ "value" : "Fragezeichen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pregunta" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -32573,12 +27965,6 @@ }, "Radiation" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Radiación" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -32613,12 +27999,6 @@ "value" : "Geräteeinstellungen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración de radio" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -32683,12 +28063,6 @@ "value" : "RAK Drehimpulsgeber Modul" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Codificador rotatorio RAK" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -32753,12 +28127,6 @@ "value" : "Entfernungstest" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Prueba de rango" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -32823,12 +28191,6 @@ "value" : "Entfernungstest Konfiguration" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración de prueba de rango" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -32893,12 +28255,6 @@ "value" : "Range Test Modul konfiguration empfangen: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración del módulo de prueba de rango recibida: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -32963,12 +28319,6 @@ "value" : "Neustart" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Reiniciar" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -33033,12 +28383,6 @@ "value" : "Knoten neustarten?" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "¿Reiniciar el nodo?" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -33097,12 +28441,6 @@ }, "Rebroadcast any observed message, if it was on our private channel or from another mesh with the same lora params." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Retransmitir cualquier mensaje observado, si fue en nuestro canal privado o desde otra malla con los mismos parámetros de lora." - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -33125,12 +28463,6 @@ }, "Rebroadcast Mode" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Modo de retransmisión" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -33165,12 +28497,6 @@ }, "Receive data (rxd) GPIO pin" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Recibir datos (rxd) pin GPIO" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -33205,12 +28531,6 @@ "value" : "Negative Empfangsbestätigung empfangen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Recibí un reconocimiento negativo." - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -33276,12 +28596,6 @@ "value" : "Empfangsbestätigung" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Confirmación recibida" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -33350,12 +28664,6 @@ "value" : "Recipient Ack" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Confirmación del destinatario" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -33423,12 +28731,6 @@ "value" : "Route aufzeichnen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ruta de grabación" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -33457,12 +28759,6 @@ }, "Refresh device metadata" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Actualizar metadatos del dispositivo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -33491,12 +28787,6 @@ }, "Regenerate Private Key" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Regenerar clave privada" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -33519,12 +28809,6 @@ "value" : "Region" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Región" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -33565,12 +28849,6 @@ "value" : "Regionale Einschaltdauergrenze erreicht" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Se alcanzó el límite del ciclo de trabajo regional" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -33639,12 +28917,6 @@ }, "Release Notes" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Notas de la versión" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -33673,12 +28945,6 @@ }, "Remote administration for: %@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Administración remota para: %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -33707,12 +28973,6 @@ }, "Remote Legacy Admin: %@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Administrador remoto heredado: %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -33741,12 +29001,6 @@ }, "Remote PKI Admin: %@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Administrador de PKI remoto: %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -33781,12 +29035,6 @@ "value" : "Entfernen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Eliminar" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -33821,12 +29069,6 @@ "value" : "Von Favoriten entfernen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Quitar de favoritos" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -33855,12 +29097,6 @@ }, "Remove from ignored" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Quitar de ignorado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -33901,12 +29137,6 @@ "value" : "Repeater" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Reloj de repetición" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -33941,12 +29171,6 @@ }, "Replace Channels" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Reemplazar canales" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -33981,12 +29205,6 @@ "value" : "Antworten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Responder" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -34045,12 +29263,6 @@ }, "Request Legacy Admin: %@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Solicitar administrador heredado: %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -34079,12 +29291,6 @@ }, "Request PKI Admin: %@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Solicitar administrador de PKI: %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -34113,12 +29319,6 @@ }, "Requested Canned Messages Module Messages for node: %@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mensajes del módulo de mensajes predefinidos solicitados para el nodo: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -34177,12 +29377,6 @@ }, "Requires that there be an accelerometer on your device." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Requiere que haya un acelerómetro en su dispositivo." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -34217,12 +29411,6 @@ "value" : "App-Einstellungen zurücksetzen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Restablecer la configuración de la aplicación" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -34263,12 +29451,6 @@ "value" : "Knotendatenbank zurücksetzen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Restablecer NodeDB" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -34303,12 +29485,6 @@ "value" : "Neustarten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Reanudar" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -34343,12 +29519,6 @@ "value" : "Verbundenen Knoten neustarten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Reinicie en el nodo al que está conectado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -34377,12 +29547,6 @@ }, "Restore" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Restaurar" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -34405,12 +29569,6 @@ "value" : "Fortsetzen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Reanudar" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -34469,12 +29627,6 @@ }, "Retreiving nodes . ." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Recuperando nodos. ." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -34485,12 +29637,6 @@ }, "Retreiving nodes %lld" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Recuperando nodos %lld" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -34501,12 +29647,6 @@ }, "Retrieving nodes" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Recuperando nodos" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -34517,12 +29657,6 @@ }, "Retrieving nodes %lld" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Recuperando nodos %lld" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -34533,12 +29667,6 @@ }, "Retrying (attempt %lld)" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Reintentando (intento %lld)" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -34555,12 +29683,6 @@ "value" : "App bewerten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Revisa la aplicación" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -34601,12 +29723,6 @@ "value" : "Rechts" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bien" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -34671,12 +29787,6 @@ "value" : "Klingelton" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tono de llamada" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -34741,12 +29851,6 @@ "value" : "Klingelton Konfiguration" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración de tono de llamada" - } - }, "he" : { "stringUnit" : { "state" : "translated", @@ -34799,12 +29903,6 @@ }, "Ringtone Transfer Language" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Idioma de transferencia de tono de llamada" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -34845,12 +29943,6 @@ }, "Ringtone Transfer Language(RTTTL) Ringtone String used by supported buzzers in external notifications." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Lenguaje de transferencia de tono de llamada (RTTTL) Cadena de tono utilizada por los timbres compatibles en notificaciones externas." - } - }, "he" : { "stringUnit" : { "state" : "translated", @@ -34909,12 +30001,6 @@ "value" : "Rolle" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Role" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -34955,12 +30041,6 @@ "value" : "Rolle: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Role: %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -34995,12 +30075,6 @@ "value" : "Rollen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Roles" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -35029,12 +30103,6 @@ }, "Root Topic" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tema raíz" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -35063,12 +30131,6 @@ }, "Rotary 1" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Giratorio 1" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -35103,12 +30165,6 @@ }, "Route Back: %@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ruta de regreso: %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -35137,12 +30193,6 @@ }, "Route Lines" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Líneas de ruta" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -35177,12 +30227,6 @@ "value" : "Routenliste" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Lista de rutas" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -35199,12 +30243,6 @@ "value" : "Route aufzeichnen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Grabador de ruta" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -35245,12 +30283,6 @@ "value" : "Routenaufzeichnung pausiert" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Grabación de ruta en pausa" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -35285,12 +30317,6 @@ "value" : "Route: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ruta: %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -35325,12 +30351,6 @@ "value" : "Router" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enrutador" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -35371,12 +30391,6 @@ "value" : "Router mit Verzögerung" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enrutador tarde" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -35411,12 +30425,6 @@ "value" : "Routenliste" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rutas" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -35457,12 +30465,6 @@ "value" : "Routing empfangen für RequestID: %@ Ack Status: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enrutamiento recibido para RequestID: %@ Estado de confirmación: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -35521,12 +30523,6 @@ }, "RSSI %@ dBm" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "RSSI %@dBm" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -35555,12 +30551,6 @@ }, "RSSI %ddB" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "RSSI %ddB" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -35589,12 +30579,6 @@ }, "RSSI %llddB" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "RSSI %llddB" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -35629,12 +30613,6 @@ "value" : "RTTTL Klingeltonkonfiguration empfangen: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración de tono de llamada RTTTL recibida: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -35693,12 +30671,6 @@ }, "Russia" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rusia" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -35721,12 +30693,6 @@ }, "RX Boosted Gain" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ganancia impulsada por RX" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -35755,12 +30721,6 @@ }, "Same as behavior as ALL but skips packet decoding and simply rebroadcasts them. Only available in Repeater role. Setting this on any other roles will result in ALL behavior." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Igual que el comportamiento de ALL, pero omite la decodificación de paquetes y simplemente los retransmite. Sólo disponible en rol de Repetidor. Establecer esto en cualquier otro rol dará como resultado TODOS los comportamientos." - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -35789,12 +30749,6 @@ "value" : "Satellit" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Satélite" - } - }, "he" : { "stringUnit" : { "state" : "translated", @@ -35841,12 +30795,6 @@ }, "Satellite Flyover" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sobrevuelo satelital" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -35911,12 +30859,6 @@ "value" : "Satelliten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "sábados" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -35951,12 +30893,6 @@ "value" : "Satelliten Schätzung %lld" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Estimación de sats %lld" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -35991,12 +30927,6 @@ "value" : "Satelliten in Sicht: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sats a la vista: %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -36031,12 +30961,6 @@ "value" : "Speichern" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ahorrar" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -36095,12 +31019,6 @@ }, "Save Channel Settings" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Guardar configuración del canal" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -36135,12 +31053,6 @@ "value" : "Speichere Konfiguration für %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Guardar configuración para %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -36205,12 +31117,6 @@ "value" : "Benutzerkonfiguration nach %@ speichern?" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "¿Guardar configuración de usuario en %@?" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -36245,12 +31151,6 @@ }, "Saves a CSV with the range test message details, currently only available on ESP32 devices with a web server." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Guarda un CSV con los detalles del mensaje de prueba de rango, actualmente solo disponible en dispositivos ESP32 con un servidor web." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -36285,12 +31185,6 @@ }, "Scan this QR code to add %@ to another device." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Escanee este código QR para agregar %@ a otro dispositivo." - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -36313,12 +31207,6 @@ }, "Screen on for" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pantalla encendida para" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -36353,12 +31241,6 @@ "value" : "Suchen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Buscar" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -36387,12 +31269,6 @@ }, "Second" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Segundo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -36427,12 +31303,6 @@ "value" : "Sekundär" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Secundario" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -36497,12 +31367,6 @@ "value" : "Zweiter Admin-Schlüssel" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Clave de administrador secundario" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -36543,12 +31407,6 @@ "value" : "Sicherheit" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Seguridad" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -36589,12 +31447,6 @@ "value" : "Sicherheitskonfiguration" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración de seguridad" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -36635,12 +31487,6 @@ "value" : "Sicherheitskonfigurationseinstellungen erfordern eine Firmware mit Version 2.5 oder höher" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Los ajustes de configuración de seguridad requieren una versión de firmware 2.5+" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -36681,12 +31527,6 @@ "value" : "Auswählen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Seleccionar" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -36751,12 +31591,6 @@ "value" : "Kanal wählen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Seleccione un canal" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -36785,12 +31619,6 @@ }, "Select a conversation" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Seleccione una conversación" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -36819,12 +31647,6 @@ }, "Select a conversation type" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Seleccione un tipo de conversación" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -36858,23 +31680,10 @@ } }, "Select a Node" : { - "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Seleccione un nodo" - } - } - } + }, "Select a node from the drop down to manage connected or remote devices." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Seleccione un nodo del menú desplegable para administrar dispositivos conectados o remotos." - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -36897,12 +31706,6 @@ }, "Select a Trace Route" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Seleccione una ruta de seguimiento" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -36934,12 +31737,6 @@ }, "Select Channel" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Seleccionar canal" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -36974,12 +31771,6 @@ "value" : "Datei auswählen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Seleccionar archivo de mapa" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -36996,12 +31787,6 @@ "value" : "Als kritisch eingestufte Mitteilungen ignorieren den Stummschalter und die 'Nicht stören'-Einstellungen des Benachrichtigungszentrums." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Los paquetes seleccionados enviados como críticos ignorarán el interruptor de silencio y la configuración de No molestar en el centro de notificaciones del sistema operativo." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -37018,12 +31803,6 @@ "value" : "Senden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enviar" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -37058,12 +31837,6 @@ "value" : "Sende ${messageContent} an ${channelNumber}" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enviar ${messageContent} a ${channelNumber}" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -37092,12 +31865,6 @@ }, "Send ${messageContent} to ${nodeNumber}" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enviar ${messageContent} a ${nodeNumber}" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -37126,12 +31893,6 @@ }, "Send a Direct Message" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enviar un mensaje directo" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -37148,12 +31909,6 @@ "value" : "Gruppennachricht senden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enviar un mensaje grupal" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -37182,12 +31937,6 @@ }, "Send a heartbeat to advertise the server's presence." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Envíe un latido para anunciar la presencia del servidor." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -37216,12 +31965,6 @@ }, "Send a message to a certain meshtastic channel" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enviar un mensaje a un determinado canal meshtastic" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -37250,12 +31993,6 @@ }, "Send a message to a certain meshtastic node" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enviar un mensaje a un determinado nodo meshtastic" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -37266,12 +32003,6 @@ }, "Send a position on the primary channel when the user button is triple clicked." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Envíe una posición en el canal principal cuando se haga triple clic en el botón del usuario." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -37312,12 +32043,6 @@ "value" : "Herunterfahren an verbundenen Knoten senden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Envía un apagado al nodo al que estás conectado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -37352,12 +32077,6 @@ "value" : "Wegpunkt senden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enviar un punto de ruta" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -37386,12 +32105,6 @@ }, "Send ASCII bell with alert message. Useful for triggering external notification on bell." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enviar campana ASCII con mensaje de alerta. Útil para activar notificaciones externas al tocar el timbre." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -37432,12 +32145,6 @@ "value" : "Sende Glocke" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enviar campana" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -37478,12 +32185,6 @@ "value" : "Herzschlag senden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enviar latido" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -37548,12 +32249,6 @@ "value" : "Mitteilungen senden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enviar notificaciones" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -37564,12 +32259,6 @@ }, "Send Reboot OTA" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enviar Reiniciar OTA" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -37604,12 +32293,6 @@ }, "Sender Interval" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Intervalo del remitente" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -37650,12 +32333,6 @@ "value" : "Sensor" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sensor" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -37690,12 +32367,6 @@ }, "Sensor options" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Opciones de sensores" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -37725,12 +32396,6 @@ "Sensor Options" : { "extractionState" : "stale", "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Opciones de sensores" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -37759,12 +32424,6 @@ }, "Sent a Channel for: %@ Channel Index %d" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Envió un canal para: %@ Índice de canales %d" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -37829,12 +32488,6 @@ "value" : "LoRa.Config gesendet für: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Envió un LoRa.Config para: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -37900,12 +32553,6 @@ "value" : "Position von Apple Gerät an Knoten gesendet: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Envió un paquete de posición desde el GPS del dispositivo Apple al nodo: %@@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -37970,12 +32617,6 @@ "value" : "Sende Traceroute Anforderung zu Knoten: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Envió una solicitud de ruta de seguimiento al nodo: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -38040,12 +32681,6 @@ "value" : "Wegpunkt gesendet von: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enviado un paquete de waypoint desde: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -38110,12 +32745,6 @@ "value" : "Sende Nachricht %@ von %@ an %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mensaje enviado %@ de %@ a %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -38180,12 +32809,6 @@ "value" : "Sequenznummer" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Número de secuencia" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -38220,12 +32843,6 @@ "value" : "Sequenz: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Secuencia: %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -38254,12 +32871,6 @@ }, "Serial" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "De serie" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -38324,12 +32935,6 @@ "value" : "Serial Konfiguration" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración en serie" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -38394,12 +32999,6 @@ "value" : "Serielle Konsole" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Consola serie" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -38434,12 +33033,6 @@ "value" : "Serielle Konsole über die Stream-API." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Consola serial a través de Stream API." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -38474,12 +33067,6 @@ "value" : "Serial Modul Konfiguration empfangen: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración del módulo serie recibida: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -38538,12 +33125,6 @@ }, "Series" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Serie" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -38578,12 +33159,6 @@ "value" : "Server" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Servidor" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -38618,12 +33193,6 @@ "value" : "Serveradresse" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dirección del servidor" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -38652,12 +33221,6 @@ }, "Server Option" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Opción de servidor" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -38686,12 +33249,6 @@ }, "Set" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Colocar" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -38726,12 +33283,6 @@ "value" : "Setze LoRa Region" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Establecer región LoRa" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -38790,12 +33341,6 @@ }, "Set the GPIO pins for RXD and TXD." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configure los pines GPIO para RXD y TXD." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -38824,12 +33369,6 @@ }, "Set to current location" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Establecer en la ubicación actual" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -38846,12 +33385,6 @@ }, "Sets the maximum number of hops, default is 3. Increasing hops also increases congestion and should be used carefully. O hop broadcast messages will not get ACKs." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Establece el número máximo de saltos; el valor predeterminado es 3. El aumento de saltos también aumenta la congestión y debe usarse con cuidado. Los mensajes de difusión de O hop no recibirán ACK." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -38880,12 +33413,6 @@ }, "Sets the screen clock format to 12-hour." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Establece el formato del reloj de la pantalla en 12 horas." - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -38908,12 +33435,6 @@ "value" : "Einstellungen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "ajustes" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -38930,12 +33451,6 @@ "value" : "Einstellungen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ajustes" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -39000,12 +33515,6 @@ "value" : "Zweiundsiebzig Stunden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Setenta y dos horas" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -39064,12 +33573,6 @@ }, "Share Contact QR" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Compartir Contacto QR" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -39098,12 +33601,6 @@ "value" : "Standort teilen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Compartir ubicación" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -39120,12 +33617,6 @@ "value" : "Kanal QR Code teilen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Compartir código QR" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -39190,12 +33681,6 @@ "value" : "QR Code & Link teilen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Compartir código QR y enlace" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -39230,12 +33715,6 @@ "value" : "Teile deinen Standort in Echtzeit und koordiniere deine Gruppe mithilfe integrierter GPS-Funktionen." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Comparta su ubicación en tiempo real y mantenga a su grupo coordinado con funciones de GPS integradas." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -39252,12 +33731,6 @@ "value" : "Gemeinsamer Schlüssel" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Clave compartida" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -39292,12 +33765,6 @@ "value" : "Meshtastic Kanäle teilen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Compartir canales Meshtastic" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -39362,12 +33829,6 @@ "value" : "Kurzname" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nombre corto" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -39402,12 +33863,6 @@ }, "Short Range - Fast" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Corto alcance - Rápido" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -39436,12 +33891,6 @@ }, "Short Range - Slow" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Corto alcance - Lento" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -39470,12 +33919,6 @@ }, "Short Range - Turbo" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Corto alcance - Turbo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -39504,12 +33947,6 @@ }, "Show a confirmation dialog before performing the factory reset" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mostrar un cuadro de diálogo de confirmación antes de realizar el restablecimiento de fábrica" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -39526,12 +33963,6 @@ "value" : "Zeige Alarme" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mostrar alertas" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -39566,12 +33997,6 @@ "value" : "Zeige Alarme" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mostrar alertas" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -39606,12 +34031,6 @@ "value" : "Zeige Knoten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mostrar nodos" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -39646,12 +34065,6 @@ "value" : "Zeige auf dem Gerätebildschirm" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mostrar en la pantalla del dispositivo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -39686,12 +34099,6 @@ "value" : "Zeige auf der Netzwerkkarte." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mostrar en el mapa de malla." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -39727,12 +34134,6 @@ "value" : "Zeige Wegpunkte" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mostrar puntos de ruta" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -39761,12 +34162,6 @@ }, "Shows information for the connected Lora radio. You can swipe left to disconnect the radio and long press to start the live activity." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Muestra información de la radio Lora conectada. Puede deslizar hacia la izquierda para desconectar la radio y mantener presionada para iniciar la actividad en vivo." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -39783,12 +34178,6 @@ "value" : "Herunterfahren" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cerrar" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -39823,12 +34212,6 @@ "value" : "Knoten herunterfahren?" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "¿Cerrar el nodo?" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -39863,12 +34246,6 @@ "value" : "Knoten herunterfahren?" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "¿Apagar el nodo?" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -39903,12 +34280,6 @@ "value" : "Herunterfahren bei Stromunterbruch" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Apagado por pérdida de energía" - } - }, "he" : { "stringUnit" : { "state" : "translated", @@ -39961,12 +34332,6 @@ }, "Signal %@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Señal %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -40001,12 +34366,6 @@ "value" : "Einfach" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Simple" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -40065,12 +34424,6 @@ }, "Singapore 923MHz" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Singapur 923MHz" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -40105,12 +34458,6 @@ "value" : "Sechs Stunden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Seis horas" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -40175,12 +34522,6 @@ "value" : "Skifahren" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Esquiar" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -40215,12 +34556,6 @@ }, "Smart Position" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Posición inteligente" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -40249,12 +34584,6 @@ }, "SNR" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "SNR" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -40283,12 +34612,6 @@ }, "SNR %@ dB" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "SNR %@dB" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -40317,12 +34640,6 @@ }, "SNR %@dB" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "SNR %@dB" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -40351,12 +34668,6 @@ }, "Soil Moisture" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Humedad del suelo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -40385,12 +34696,6 @@ }, "Soil Temp" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Temperatura del suelo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -40420,12 +34725,6 @@ "Specifies how long the monitored GPIO should output." : { "extractionState" : "stale", "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Especifica cuánto tiempo debe emitir el GPIO monitoreado." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -40460,12 +34759,6 @@ "value" : "Geschwindigkeit" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Velocidad" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -40500,12 +34793,6 @@ "value" : "Geschwindigkeit %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Velocidad %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -40540,12 +34827,6 @@ "value" : "Geschwindigkeit: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Velocidad: %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -40580,12 +34861,6 @@ "value" : "App-Entwicklung unterstützen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Desarrollo de aplicaciones para patrocinadores" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -40602,12 +34877,6 @@ }, "Spread Factor" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Factor de dispersión" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -40642,12 +34911,6 @@ "value" : "SSID" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "SSID" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -40706,12 +34969,6 @@ }, "Standard" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Estándar" - } - }, "he" : { "stringUnit" : { "state" : "translated", @@ -40758,12 +35015,6 @@ }, "Standard Muted" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Estándar silenciado" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -40828,12 +35079,6 @@ "value" : "Start" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Comenzar" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -40892,12 +35137,6 @@ }, "State Broadcast Interval" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Intervalo de transmisión estatal" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -40932,12 +35171,6 @@ "value" : "Überall in Verbindung bleiben" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Manténgase conectado en cualquier lugar" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -40948,12 +35181,6 @@ }, "Store & Forward" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Almacenar y reenviar" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -40988,12 +35215,6 @@ }, "Store & Forward Config" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Almacenar y reenviar configuración" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -41028,12 +35249,6 @@ }, "Store & Forward module config received: %@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración del módulo Store & Forward recibida: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -41092,12 +35307,6 @@ }, "Store and forward servers require an ESP32 device with PSRAM or Linux Native." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Los servidores de almacenamiento y reenvío requieren un dispositivo ESP32 con PSRAM o Linux Native." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -41126,12 +35335,6 @@ }, "Subscribed" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "suscrito" - } - }, "he" : { "stringUnit" : { "state" : "translated", @@ -41166,12 +35369,6 @@ }, "Subsystem" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "subsistema" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -41212,12 +35409,6 @@ "value" : "Successfully uploaded '%1$@' with %2$lld overlays" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "'%@' subido correctamente con %lld superposiciones" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -41234,12 +35425,6 @@ "value" : "Unterstützt" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Apoyado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -41268,12 +35453,6 @@ }, "Supported I2C Connected sensors will be detected automatically, sensors are BMP280, BME280, BME680, MCP9808, INA219, INA260, LPS22 and SHTC3." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Los sensores conectados I2C compatibles se detectarán automáticamente, los sensores son BMP280, BME280, BME680, MCP9808, INA219, INA260, LPS22 y SHTC3." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -41308,12 +35487,6 @@ }, "Table" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mesa" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -41342,12 +35515,6 @@ }, "Taiwan" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Taiwán" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -41376,12 +35543,6 @@ "value" : "TAK" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "NO" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -41422,12 +35583,6 @@ "value" : "TAK Tracker" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "SÍ rastreador" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -41462,12 +35617,6 @@ }, "Takes a Meshtastic channel URL and saves the channel settings." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Toma la URL de un canal Meshtastic y guarda la configuración del canal." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -41496,12 +35645,6 @@ }, "Takes a Meshtastic contact URL and saves it to the nodes database" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Toma una URL de contacto Meshtastic y la guarda en la base de datos de nodos" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -41530,12 +35673,6 @@ "value" : "Tapback Antwort" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tapback" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -41593,14 +35730,6 @@ } }, "TCP" : { - "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "tcp" - } - } - }, "shouldTranslate" : false }, "Telemetry" : { @@ -41611,12 +35740,6 @@ "value" : "Telemetrie (Sensoren)" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Telemetria" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -41681,12 +35804,6 @@ "value" : "Telemetrie Einstellungen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración de telemetría" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -41751,12 +35868,6 @@ "value" : "Telemetrie Modul Konfiguration empfangen: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración del módulo de telemetría recibida: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -41821,12 +35932,6 @@ "value" : "Temp" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Temperatura" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -41861,12 +35966,6 @@ "value" : "Temperatur" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Temperatura" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -41901,12 +36000,6 @@ "value" : "Zehn Minuten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "diez minutos" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -41971,12 +36064,6 @@ "value" : "Zehn Sekunden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Diez segundos" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -42041,12 +36128,6 @@ "value" : "Dritter Admin-Schlüssel" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Clave de administrador terciario" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -42087,12 +36168,6 @@ "value" : "Textnachricht" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mensaje de texto" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -42151,12 +36226,6 @@ }, "TFT Full Color Displays" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pantallas TFT a todo color" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -42185,12 +36254,6 @@ }, "Thailand" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tailandia" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -42219,12 +36282,6 @@ }, "The amount of time to wait before we consider your packet as done." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "La cantidad de tiempo que debemos esperar antes de que consideremos que su paquete está listo." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -42253,12 +36310,6 @@ }, "The compass heading on the screen outside of the circle will always point north." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "El rumbo de la brújula en la pantalla fuera del círculo siempre apuntará al norte." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -42293,12 +36344,6 @@ "value" : "Der Taupunkt ist gerade %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "El punto de rocío es %@ en este momento." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -42327,12 +36372,6 @@ }, "The fastest that position updates will be sent if the minimum distance has been satisfied" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Lo más rápido que se enviarán las actualizaciones de posición si se ha cumplido la distancia mínima" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -42367,12 +36406,6 @@ "value" : "Die letzten 4 Zeichen der MAC-Adresse des Geräts werden an den Kurznamen angehängt, um den BLE-Namen des Geräts festzulegen. Der Kurzname kann bis zu 4 Byte lang sein." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Los últimos 4 de la dirección MAC del dispositivo se agregarán al nombre corto para configurar el nombre BLE del dispositivo. El nombre corto puede tener hasta 4 bytes de longitud." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -42407,12 +36440,6 @@ }, "The maximum interval that can elapse without a node broadcasting a position" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "El intervalo máximo que puede transcurrir sin que un nodo transmita una posición." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -42441,12 +36468,6 @@ }, "The Meshtastic Apple apps support firmware version %@ and above." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Las aplicaciones Meshtastic de Apple admiten la versión de firmware %@ y superior." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -42481,12 +36502,6 @@ }, "The minimum distance change in meters to be considered for a smart position broadcast." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "El cambio mínimo de distancia en metros a considerar para una transmisión de posición inteligente." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -42527,12 +36542,6 @@ "value" : "Das Paket ist zu groß" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "El paquete es demasiado grande." - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -42597,12 +36606,6 @@ "value" : "Der erste öffentliche Schlüssel, der berechtigt ist, Admin-Nachrichten an diesen Knoten zu senden." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "La clave pública principal autorizada para enviar mensajes de administrador a este nodo." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -42643,12 +36646,6 @@ "value" : "Die Region, in der du deine Funkgeräte verwenden wirst." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "La región donde utilizará sus radios." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -42683,12 +36680,6 @@ }, "The root topic to use for MQTT." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "El tema raíz que se utilizará para MQTT." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -42723,12 +36714,6 @@ }, "The Router roles are only for high vantage locations like mountaintops and towers with few nearby nodes, not for use in urban areas. Improper use will hurt your local mesh." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Las funciones de enrutador son solo para ubicaciones estratégicas, como cimas de montañas y torres con pocos nodos cercanos, no para uso en áreas urbanas. El uso inadecuado dañará su malla local." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -42745,12 +36730,6 @@ "value" : "Der zweite öffentliche Schlüssel, der berechtigt ist, Admin-Nachrichten an diesen Knoten zu senden." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "La clave pública secundaria autorizada para enviar mensajes de administrador a este nodo." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -42791,12 +36770,6 @@ "value" : "Status der LED (an/aus)" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "El estado del LED (encendido/apagado)" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -42831,12 +36804,6 @@ "value" : "Der dritte öffentliche Schlüssel, der berechtigt ist, Admin-Nachrichten an diesen Knoten zu senden." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "La clave pública terciaria autorizada para enviar mensajes de administrador a este nodo." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -42871,12 +36838,6 @@ }, "The URL for the channel settings" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "La URL para la configuración del canal." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -42905,12 +36866,6 @@ }, "The URL for the node to add" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "La URL del nodo a agregar." - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -42933,12 +36888,6 @@ }, "There has been no response to a request for device metadata via PKC admin for this node." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "No ha habido respuesta a una solicitud de metadatos del dispositivo a través del administrador de PKC para este nodo." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -42949,12 +36898,6 @@ }, "There is an issue with this contact's public key." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Hay un problema con la clave pública de este contacto." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -42970,12 +36913,6 @@ "These settings will %@ channels. The current LoRa Config will be replaced, if there are substantial changes to the LoRa config the device will reboot" : { "extractionState" : "stale", "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Estas configuraciones %@ canales. La configuración LoRa actual será reemplazada; si hay cambios sustanciales en la configuración LoRa, el dispositivo se reiniciará" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -43010,12 +36947,6 @@ "value" : "Dreißig Minuten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "treinta minutos" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -43080,12 +37011,6 @@ "value" : "Dreißig Sekunden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "treinta segundos" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -43150,12 +37075,6 @@ "value" : "Sechsunddreissig Stunden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "treinta y seis horas" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -43214,12 +37133,6 @@ }, "This conversation will be deleted." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Esta conversación será eliminada." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -43248,12 +37161,6 @@ }, "This could take a while, response will appear in the trace route log for the node it was sent to." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Esto podría tardar un poco; la respuesta aparecerá en el registro de ruta de seguimiento del nodo al que se envió." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -43282,12 +37189,6 @@ }, "This device will send out range test messages on the selected interval." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Este dispositivo enviará mensajes de prueba de alcance en el intervalo seleccionado." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -43328,12 +37229,6 @@ "value" : "Diese Nachricht wurde höchstwahrscheinlich nicht übermittelt." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Es probable que este mensaje no se haya entregado." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -43362,12 +37257,6 @@ }, "This node does not support any configurable modules." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Este nodo no admite ningún módulo configurable." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -43396,12 +37285,6 @@ }, "This will disable fixed position and remove the currently set position." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Esto desactivará la posición fija y eliminará la posición establecida actualmente." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -43430,12 +37313,6 @@ }, "This will send a current position from your phone and enable fixed position." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Esto enviará una posición actual desde su teléfono y habilitará la posición fija." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -43476,12 +37353,6 @@ "value" : "Drei Stunden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "tres horas" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -43546,12 +37417,6 @@ "value" : "Drei Sekunden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "tres segundos" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -43616,12 +37481,6 @@ "value" : "Daumen runter" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pulgar hacia abajo" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -43686,12 +37545,6 @@ "value" : "Daumen hoch" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pulgares hacia arriba" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -43756,12 +37609,6 @@ "value" : "Zeit" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tiempo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -43796,12 +37643,6 @@ "value" : "Zeitstempel" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Marca de tiempo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -43836,12 +37677,6 @@ "value" : "Zeitzone" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Huso horario" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -43876,12 +37711,6 @@ "value" : "Zeitzone für Daten auf dem Gerätebildschirm und Log." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Zona horaria para fechas en la pantalla del dispositivo y registro." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -43916,12 +37745,6 @@ "value" : "Zeitlimit erreicht" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Se acabó el tiempo" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -43986,12 +37809,6 @@ "value" : "Zeitstempel" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Marca de tiempo" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -44050,12 +37867,6 @@ }, "Timing and Overrides" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Temporización y anulaciones" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -44066,12 +37877,6 @@ }, "TLS Enabled" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "TLS habilitado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -44106,12 +37911,6 @@ }, "To comply with privacy laws like CCPA and GDPR, we avoid sharing exact location data. Instead, we use anonymized or approximate (imprecise) location information to protect your privacy." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Para cumplir con las leyes de privacidad como CCPA y GDPR, evitamos compartir datos de ubicación exacta. En su lugar, utilizamos información de ubicación anónima o aproximada (imprecisa) para proteger su privacidad." - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -44134,12 +37933,6 @@ }, "To Radio (TX): %lld" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "A radio (TX): %lld" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -44153,12 +37946,6 @@ }, "Topic: %@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tema: %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -44193,12 +37980,6 @@ "value" : "Total" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Total" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -44227,12 +38008,6 @@ }, "Total PAX" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "PAX TOTALES" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -44267,12 +38042,6 @@ }, "Trace Route" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ruta de seguimiento" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -44301,12 +38070,6 @@ }, "Trace Route (in %@s)" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ruta de seguimiento (en %@s)" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -44329,12 +38092,6 @@ }, "Trace Route Log" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Registro de ruta de seguimiento" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -44375,12 +38132,6 @@ "value" : "Traceroute Ergebnis: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Solicitud de ruta de seguimiento devuelta: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -44439,12 +38190,6 @@ }, "Trace Route Sent" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ruta de seguimiento enviada" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -44473,12 +38218,6 @@ }, "Trace route sent to %@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ruta de seguimiento enviada a %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -44507,12 +38246,6 @@ }, "Trace route to %@ was not sent." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "La ruta de seguimiento a %@ no se envió." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -44541,12 +38274,6 @@ }, "Trace Route was rate limited. You can send a trace route a maximum of once every thirty seconds." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Trace Route tenía una tarifa limitada. Puede enviar una ruta de rastreo como máximo una vez cada treinta segundos." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -44581,12 +38308,6 @@ "value" : "Standorte verfolgen und teilen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Seguimiento y compartir ubicaciones" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -44597,12 +38318,6 @@ }, "Tracker" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rastreador" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -44637,12 +38352,6 @@ "value" : "Verkehr" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tráfico" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -44671,12 +38380,6 @@ }, "Transmit data (txd) GPIO pin" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Transmitir datos (txd) pin GPIO" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -44705,12 +38408,6 @@ }, "Transmit Enabled" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Transmisión habilitada" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -44745,12 +38442,6 @@ }, "Treat double tap on supported accelerometers as a user button press." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Considere el doble toque en los acelerómetros compatibles como si el usuario presionara un botón." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -44785,12 +38476,6 @@ }, "TriggerType" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tipo de disparador" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -44819,12 +38504,6 @@ }, "Triple Click Ad Hoc Ping" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ping ad hoc de triple clic" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -44859,12 +38538,6 @@ "value" : "Erneut versuchen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Intentar otra vez" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -44899,12 +38572,6 @@ "value" : "Zwölf Stunden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Doce horas" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -44969,12 +38636,6 @@ "value" : "Vierundzwanzig Stunden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "veinticuatro horas" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -45039,12 +38700,6 @@ "value" : "Zwei Stunden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "dos horas" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -45109,12 +38764,6 @@ "value" : "Zwei Minutes" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "dos minutos" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -45179,12 +38828,6 @@ "value" : "Zwei Sekunden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "dos segundos" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -45243,12 +38886,6 @@ }, "UDP Broadcast" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Transmisión UDP" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -45277,12 +38914,6 @@ }, "Ukraine 433MHz" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ucrania 433MHz" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -45311,12 +38942,6 @@ }, "Ukraine 868MHz" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ucrania 868MHz" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -45345,12 +38970,6 @@ }, "Un-Favorite" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "No favorito" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -45379,12 +38998,6 @@ }, "Unhealthy" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Malsano" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -45413,12 +39026,6 @@ }, "Unhealthy for Sensitive Groups" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "No saludable para grupos sensibles" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -45447,12 +39054,6 @@ }, "United States" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Estados Unidos" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -45481,12 +39082,6 @@ }, "Units displayed on the device screen" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Unidades mostradas en la pantalla del dispositivo." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -45515,12 +39110,6 @@ }, "unknown" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "desconocido" - } - }, "it" : { "stringUnit" : { "state" : "needs_review", @@ -45549,12 +39138,6 @@ }, "Unknown" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Desconocido" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -45619,12 +39202,6 @@ "value" : "Unbekanntes alter" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Edad desconocida" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -45689,12 +39266,6 @@ "value" : "Nicht benachrichtigbar" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "inmensable" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -45711,12 +39282,6 @@ }, "Unmonitored" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "No monitoreado" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -45739,12 +39304,6 @@ "value" : "Unset" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Desarmado" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -45803,12 +39362,6 @@ }, "Unsupported" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "No compatible" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -45843,12 +39396,6 @@ "value" : "Hoch" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Arriba" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -45907,12 +39454,6 @@ }, "Up Down 1" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "arriba abajo 1" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -45945,24 +39486,8 @@ } } }, - "UPDATE IN" : { - "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "ACTUALIZAR EN" - } - } - } - }, "Update Interval" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Intervalo de actualización" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -45997,12 +39522,6 @@ "value" : "Firmware aktualisieren" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Actualice su firmware" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -46061,12 +39580,6 @@ }, "Updated Node Stats Data." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Datos de estadísticas de nodos actualizados." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -46101,12 +39614,6 @@ "value" : "Aktualisiert: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Actualizado: %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -46135,12 +39642,6 @@ }, "Uplink Enabled" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enlace ascendente habilitado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -46181,12 +39682,6 @@ "value" : "Hochladen fehlgeschlagen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Error de carga" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -46227,12 +39722,6 @@ "value" : "Lade GeoJSON-Dateien hoch, um eigene Karten-Overlays anzuzeigen. Die Dateien werden lokal gespeichert und dürfen bis zu 10 MB groß sein." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cargue archivos GeoJSON para mostrar superposiciones de mapas personalizados. Los archivos se almacenan localmente y pueden tener hasta 10 MB." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -46268,12 +39757,6 @@ "Upload Map Data" : { "comment" : "Title for map data upload screen", "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cargar datos del mapa" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -46315,12 +39798,6 @@ "value" : "Lade Kartendaten hoch, um Overlays zu aktivieren" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cargar datos de mapas para habilitar superposiciones" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -46361,12 +39838,6 @@ "value" : "Kartendaten hochladen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cargar superposiciones de mapas" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -46383,12 +39854,6 @@ "value" : "Hochladen erfolgreich" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Subir con éxito" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -46429,12 +39894,6 @@ "value" : "Hochgeladene Kartendaten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Superposiciones de mapas cargados" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -46445,12 +39904,6 @@ }, "Uptime" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "tiempo de actividad" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -46497,12 +39950,6 @@ "value" : "Telemetriedaten erfassen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Datos de uso y fallos" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -46513,12 +39960,6 @@ }, "Use a PWM output (like the RAK Buzzer) for tunes instead of an on/off output. This will ignore the output, output duration and active settings and use the device config buzzer GPIO option instead." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Utilice una salida PWM (como el RAK Buzzer) para melodías en lugar de una salida de encendido/apagado. Esto ignorará la salida, la duración de la salida y la configuración activa y en su lugar utilizará la opción GPIO del zumbador de configuración del dispositivo." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -46553,12 +39994,6 @@ }, "Use I2S As Buzzer" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Utilice I2S como zumbador" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -46593,12 +40028,6 @@ "value" : "Standort verwenden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Usar mi ubicación" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -46621,12 +40050,6 @@ "value" : "Voreinstellung verwenden" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Usar preajuste" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -46661,12 +40084,6 @@ }, "Use PWM Buzzer" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Usar zumbador PWM" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -46707,12 +40124,6 @@ "value" : "Verwende das GPS deines Handys anstelle des GPS deines Funkgeräts." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Utilice el GPS de su teléfono para enviar ubicaciones a su nodo en lugar de utilizar un GPS de hardware en su nodo." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -46723,12 +40134,6 @@ }, "Used to create a shared key with a remote device." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Se utiliza para crear una clave compartida con un dispositivo remoto." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -46763,12 +40168,6 @@ "value" : "Wird verwendet, um nicht überwachte oder Infrastrukturknoten zu identifizieren, damit Nachrichten nicht an Knoten gesendet werden, die niemals antworten werden." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Se utiliza para identificar nodos de infraestructura o no supervisados, de modo que la mensajería no esté disponible para nodos que nunca responderán." - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -46791,12 +40190,6 @@ "value" : "Benutzer" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Usuario" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -46861,12 +40254,6 @@ "value" : "Benutzerkonfiguration" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuración de usuario" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -46907,12 +40294,6 @@ "value" : "Benutzerdaten" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Detalles del usuario" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -46947,12 +40328,6 @@ }, "User Id" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Identificación de usuario" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -46997,12 +40372,6 @@ "value" : "Daten verfügbar" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Usuario subido" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -47043,12 +40412,6 @@ "value" : "Benutzername" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nombre de usuario" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -47107,12 +40470,6 @@ }, "Uses pullup resistor" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Utiliza resistencia pullup" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -47141,12 +40498,6 @@ }, "Utilizes the network connection on your phone to connect to MQTT." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Utiliza la conexión de red de su teléfono para conectarse a MQTT." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -47187,12 +40538,6 @@ "value" : "Fahrzeugsteuerkurs" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rumbo del vehículo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -47227,12 +40572,6 @@ "value" : "Fahrzeuggeschwindigkeit" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Velocidad del vehículo" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -47261,12 +40600,6 @@ }, "Verify who you are messaging with by comparing public keys in person or over the phone. The most recent public key for this node does not match the previously recorded key. You can delete the node and let it exchange keys again if the key change was due to a factory reset or other intentional action but this also may indicate a more serious security problem." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Verifique con quién está enviando mensajes comparando claves públicas en persona o por teléfono. La clave pública más reciente para este nodo no coincide con la clave registrada anteriormente. Puede eliminar el nodo y dejar que intercambie claves nuevamente si el cambio de clave se debió a un restablecimiento de fábrica u otra acción intencional, pero esto también puede indicar un problema de seguridad más grave." - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -47283,12 +40616,6 @@ "value" : "Version %1$@ includes substantial network optimizations and extensive changes to devices and client apps. Only nodes version %2$@ and above are supported." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "La versión %@ incluye optimizaciones sustanciales de la red y cambios extensos en los dispositivos y aplicaciones cliente. Solo se admiten nodos de versión %@ y superiores." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -47329,12 +40656,6 @@ "value" : "Version: %1$@ (%2$@)" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Versión: %@ (%@)" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -47363,12 +40684,6 @@ "value" : "Version: %1$@ (%2$@) " } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Versión: %1$@ (%2$@)" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -47403,12 +40718,6 @@ }, "Very Unhealthy" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "muy poco saludable" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -47443,12 +40752,6 @@ "value" : "Via Lora" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Vía Lora" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -47483,12 +40786,6 @@ "value" : "Via Mqtt" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Vía Mqtt" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -47529,12 +40826,6 @@ "value" : "Voltage" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Voltaje" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -47593,12 +40884,6 @@ }, "Volts %@" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Voltios %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -47633,12 +40918,6 @@ "value" : "Warte..." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Espera" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -47697,12 +40976,6 @@ }, "Waiting to be acknowledged. . ." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Esperando ser reconocido. . ." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -47731,12 +41004,6 @@ }, "Wake Screen on tap or motion" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Activar pantalla con un toque o movimiento" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -47771,12 +41038,6 @@ "value" : "Gehen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Caminando" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -47817,12 +41078,6 @@ "value" : "Welle" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ola" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -47857,12 +41112,6 @@ }, "Waypoint Failed to Send" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "El punto de referencia no se pudo enviar" - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -47885,12 +41134,6 @@ "value" : "Wegpunktoptionen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Opciones de punto de referencia" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -47925,12 +41168,6 @@ "value" : "Wegpunkt von Knoten empfangen: %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Paquete de waypoint recibido del nodo: %@" - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -47988,30 +41225,8 @@ } }, "Waypoints" : { - "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Puntos de referencia" - } - } - } + }, -<<<<<<< Updated upstream -======= - "We anonymously collect usage and crash data to improve the app. This helps us understand how the app is being used and where we can make improvements. The data we collect is non-personally identifiable and cannot be linked to you as an individual. You can opt out of this under app settings." : { - "comment" : "A description of how the app collects and uses user data. Includes a link to the app settings.", - "isCommentAutoGenerated" : true, - "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Recopilamos de forma anónima datos de uso y fallos para mejorar la aplicación. Esto nos ayuda a comprender cómo se utiliza la aplicación y dónde podemos realizar mejoras. Los datos que recopilamos no son identificables personalmente y no pueden vincularse a usted como individuo. Puede optar por no participar en la configuración de la aplicación." - } - } - } - }, ->>>>>>> Stashed changes "Weather Conditions" : { "localizations" : { "de" : { @@ -48020,12 +41235,6 @@ "value" : "Wetterverhältnisse" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Condiciones climáticas" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -48060,12 +41269,6 @@ }, "Web Flasher" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Intermitente web" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -48094,12 +41297,6 @@ }, "Website" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sitio web" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -48134,12 +41331,6 @@ }, "Weight" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Peso" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -48174,12 +41365,6 @@ "value" : "Willkommen bei" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bienvenido a" - } - }, "sr" : { "stringUnit" : { "state" : "translated", @@ -48196,12 +41381,6 @@ "value" : "Was bedeutet das Schloß?" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "¿Qué significa la cerradura?" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -48242,12 +41421,6 @@ "value" : "Was ist Meshtastic?" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "¿Qué es Meshtastic?" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -48282,12 +41455,6 @@ }, "What licensed operator mode does:\n* Sets the node name to your call sign \n* Broadcasts node info every 10 minutes \n* Overrides frequency, dutycycle and tx power \n* Disables encryption" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Qué hace el modo de operador con licencia:\n* Establece el nombre del nodo según su indicativo de llamada \n* Transmite información del nodo cada 10 minutos \n* Anula la frecuencia, el ciclo de trabajo y la potencia de transmisión. \n* Desactiva el cifrado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -48322,12 +41489,6 @@ }, "When enabled the PAX Counter module counts the number of people passing by using WiFi and Bluetooth. Both WiFI and Bluetooth must be disabled for PAX counter to work." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cuando está habilitado, el módulo Contador de PAX cuenta el número de personas que pasan mediante WiFi y Bluetooth. Tanto WiFI como Bluetooth deben estar desactivados para que funcione el contador de PAX." - } - }, "he" : { "stringUnit" : { "state" : "translated", @@ -48374,12 +41535,6 @@ }, "When using in GPIO mode, keep the output on for this long. " : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cuando lo use en modo GPIO, mantenga la salida encendida durante este tiempo." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -48414,12 +41569,6 @@ }, "Whether or not use INPUT_PULLUP mode for GPIO pin. Only applicable if the board uses pull-up resistors on the pin" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Si se utiliza o no el modo INPUT_PULLUP para el pin GPIO. Solo aplicable si la placa usa resistencias pull-up en el pin" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -48448,12 +41597,6 @@ }, "WiFi" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Wi-Fi" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -48494,12 +41637,6 @@ "value" : "WiFi Optionen" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Opciones WiFi" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -48528,12 +41665,6 @@ }, "Will sleep everything as much as possible, for the tracker and sensor role this will also include the lora radio. Don't use this setting if you want to use your device with the phone apps or are using a device without a user button." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dormirá todo lo más posible, para la función de rastreador y sensor esto también incluirá la radio lora. No use esta configuración si desea usar su dispositivo con las aplicaciones del teléfono o si está usando un dispositivo sin un botón de usuario." - } - }, "he" : { "stringUnit" : { "state" : "translated", @@ -48586,12 +41717,6 @@ }, "Wind" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Viento" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -48626,12 +41751,6 @@ "value" : "Windrichtung" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dirección del viento" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -48666,12 +41785,6 @@ "value" : "Windgeschwindigkeit" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Velocidad del viento" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -48706,12 +41819,6 @@ "value" : "Innerhalb %@" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dentro %@" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -48750,12 +41857,6 @@ }, "x" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "incógnita" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -48790,12 +41891,6 @@ "value" : "X: %1$@, Y: %2$d" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "X: %@, Y: %d" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -48837,12 +41932,6 @@ "value" : "X: %1$@, Y: %2$f" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "X: %@, Y: %f" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -48884,12 +41973,6 @@ "value" : "X: %1$@, Y: %2$lld" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "X: %@, Y: %lld" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -48925,12 +42008,6 @@ }, "y" : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "y" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -48975,12 +42052,6 @@ "value" : "Gestern" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ayer" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -49009,12 +42080,6 @@ }, "You can also update your Meshtastic device over bluetooth using the Nordic DFU app." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "También puede actualizar su dispositivo Meshtastic a través de bluetooth utilizando la aplicación Nordic DFU." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -49055,12 +42120,6 @@ "value" : "Du kannst Kanalnachrichten (Gruppenchats) und Direktnachrichten senden und empfangen. Bei jeder Nachricht kannst du lange drücken, um verfügbare Aktionen wie Kopieren, Antworten, Tapback und Löschen sowie Zustelldetails anzuzeigen." } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Puedes enviar y recibir canales (chats grupales) y mensajes directos. Desde cualquier mensaje, puede mantener presionado para ver las acciones disponibles como copiar, responder, retroceder y eliminar, así como los detalles de entrega." - } - }, "fr" : { "stringUnit" : { "state" : "translated", @@ -49119,12 +42178,6 @@ }, "Your current location will be set as the fixed position and broadcast over the mesh on the position interval." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Su ubicación actual se establecerá como posición fija y se transmitirá sobre la malla en el intervalo de posición." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -49165,12 +42218,6 @@ "value" : "Deine Firmware ist aktuell" } }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Su firmware está actualizado" - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -49205,12 +42252,6 @@ }, "Your MQTT Server must support TLS." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Su servidor MQTT debe admitir TLS." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -49239,12 +42280,6 @@ }, "Your node will periodically send an unencrypted map report packet to the configured MQTT server, this includes id, short and long name, approximate location, hardware model, role, firmware version, LoRa region, modem preset and primary channel name." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Su nodo enviará periódicamente un paquete de informe de mapa sin cifrar al servidor MQTT configurado, esto incluye identificación, nombre corto y largo, ubicación aproximada, modelo de hardware, función, versión de firmware, región LoRa, configuración predeterminada del módem y nombre del canal principal." - } - }, "ja" : { "stringUnit" : { "state" : "translated", @@ -49267,12 +42302,6 @@ }, "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." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "La frecuencia operativa de su nodo se calcula en función de la región, la configuración predeterminada del módem y este campo. Cuando es 0, la ranura se calcula automáticamente en función del nombre del canal principal." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -49301,12 +42330,6 @@ }, "Your position has been sent with a request for a response with their position. You will receive a notification when a position is returned." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Su posición ha sido enviada con una solicitud de respuesta con su posición. Recibirá una notificación cuando se devuelva una posición." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -49340,23 +42363,10 @@ } }, "Your public key is generated from your private key and sent to other nodes on the mesh so they can compute a shared secret key with you." : { - "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Su clave pública se genera a partir de su clave privada y se envía a otros nodos de la malla para que puedan calcular una clave secreta compartida con usted." - } - } - } + }, "Your region has a %lld%% duty cycle. MQTT is not advised when you are duty cycle restricted, the extra traffic will quickly overwhelm your LoRa mesh." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Su región tiene un ciclo de trabajo %lld%%. No se recomienda MQTT cuando tiene un ciclo de trabajo restringido, el tráfico adicional abrumará rápidamente su malla LoRa." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -49391,12 +42401,6 @@ }, "Your region has a %lld%% hourly duty cycle, your radio will stop sending packets when it reaches the hourly limit." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Su región tiene un ciclo de trabajo por hora %lld%%, su radio dejará de enviar paquetes cuando alcance el límite por hora." - } - }, "it" : { "stringUnit" : { "state" : "translated", @@ -49425,12 +42429,6 @@ }, "Your route file must have both Latitude and Longitude columns and headers." : { "localizations" : { - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Su archivo de ruta debe tener columnas y encabezados de Latitud y Longitud." - } - }, "it" : { "stringUnit" : { "state" : "translated", From 3d8887bbe9598564b4d204c919cc5bdd98fde6a5 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 4 Jan 2026 20:56:32 -0800 Subject: [PATCH 14/15] Revert "update the translations (#1540)" (#1544) This reverts commit cb2fd8cc15185f6b9ce8a940d8ca8d11a32a2f80. --- Localizable.xcstrings | 120 ++---------------------------------------- 1 file changed, 3 insertions(+), 117 deletions(-) diff --git a/Localizable.xcstrings b/Localizable.xcstrings index b47fe554..bb6de690 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -2114,12 +2114,6 @@ "value" : "О Мештастику" } }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "关于" - } - }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -7610,12 +7604,6 @@ "value" : "Очисти логове" } }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "清除Log记录" - } - }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -8334,13 +8322,7 @@ "state" : "translated", "value" : "Повежи се" } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "连接" - } - }, + } } }, "Connect to a Node" : { @@ -10354,12 +10336,6 @@ "value" : "Логови сензора откривања" } }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "检测传感器记录" - } - }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -14649,12 +14625,6 @@ "value" : "Пронађи контакт" } }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "搜索联系人" - } - }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -14689,12 +14659,6 @@ "value" : "Пронађи чвор" } }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "搜索节点" - } - }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -14982,12 +14946,6 @@ "value" : "Прво откривање" } }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "首次通信" - } - }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -15980,12 +15938,6 @@ "value" : "Апсолутна подршка" } }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "官方支持" - } - }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -16936,12 +16888,6 @@ "value" : "Сакриј упозорења" } }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "通知静音" - } - }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -20450,13 +20396,7 @@ "state" : "translated", "value" : "Ручно" } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "更多" - } - }, + } } }, "Manual Configuration" : { @@ -20983,7 +20923,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "地图" + "value" : "Mesh 地图" } }, "zh-Hant-TW" : { @@ -23844,12 +23784,6 @@ "value" : "Број чвора" } }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "用户编号" - } - }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -26663,12 +26597,6 @@ "value" : "Логови позиција" } }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "位置记录" - } - }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -27033,12 +26961,6 @@ "value" : "Логови метрике снаге" } }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "电源指标记录" - } - }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -30019,12 +29941,6 @@ "value" : "Улога" } }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "节点类型" - } - }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -30261,12 +30177,6 @@ "value" : "Снимач руте" } }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "路线记录" - } - }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -30443,12 +30353,6 @@ "value" : "Руте" } }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "路线" - } - }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -31665,12 +31569,6 @@ "value" : "Изабери тип разговора" } }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "请选择对话类型" - } - }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -38110,12 +38008,6 @@ "value" : "Лог праћења руте комуникације" } }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "追踪路由(Trace Route)记录" - } - }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -39928,12 +39820,6 @@ "value" : "Време рада" } }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "上线时长" - } - }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", From 87412c4c2a6285415f6deab0c2c267e008355aa3 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 4 Jan 2026 20:58:43 -0800 Subject: [PATCH 15/15] Revert "NFC Tag contact (#1537)" (#1545) This reverts commit 5c22b8b6e0176f4927bfc79234dabe109b215edf. --- Localizable.xcstrings | 17 +-- Meshtastic/Info.plist | 2 - Meshtastic/Meshtastic.entitlements | 4 - Meshtastic/Router/NavigationState.swift | 1 - Meshtastic/Views/Settings/Settings.swift | 9 -- Meshtastic/Views/Settings/Tools.swift | 164 ----------------------- 6 files changed, 1 insertion(+), 196 deletions(-) delete mode 100644 Meshtastic/Views/Settings/Tools.swift diff --git a/Localizable.xcstrings b/Localizable.xcstrings index bb6de690..c6603edc 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -9143,10 +9143,6 @@ } } }, - "Create Node Contact NFC Tag" : { - "comment" : "A section header that instructs the user to create a contact NFC tag.", - "isCommentAutoGenerated" : true - }, "Create Waypoint" : { "localizations" : { "de" : { @@ -23754,10 +23750,6 @@ } } }, - "Node Name: %@" : { - "comment" : "A text label displaying the name of the connected node.", - "isCommentAutoGenerated" : true - }, "Node Number" : { "localizations" : { "de" : { @@ -37838,9 +37830,6 @@ } } } - }, - "Tools" : { - }, "Topic: %@" : { "localizations" : { @@ -41737,10 +41726,6 @@ } } }, - "Write Contact to NFC Tag" : { - "comment" : "A button that writes a contact to an NFC tag.", - "isCommentAutoGenerated" : true - }, "x" : { "localizations" : { "it" : { @@ -42343,4 +42328,4 @@ } }, "version" : "1.1" -} \ No newline at end of file +} diff --git a/Meshtastic/Info.plist b/Meshtastic/Info.plist index c2cbcdd8..863fb0e9 100644 --- a/Meshtastic/Info.plist +++ b/Meshtastic/Info.plist @@ -97,8 +97,6 @@ LSSupportsOpeningDocumentsInPlace - NFCReaderUsageDescription - We use NFC tags to share node contacts NSBluetoothAlwaysUsageDescription We use bluetooth to connect to nearby Meshtastic Devices NSBluetoothPeripheralUsageDescription diff --git a/Meshtastic/Meshtastic.entitlements b/Meshtastic/Meshtastic.entitlements index a35e74ee..4dbdb836 100644 --- a/Meshtastic/Meshtastic.entitlements +++ b/Meshtastic/Meshtastic.entitlements @@ -9,10 +9,6 @@ com.apple.developer.carplay-communication - com.apple.developer.nfc.readersession.formats - - TAG - com.apple.developer.usernotifications.critical-alerts com.apple.developer.weatherkit diff --git a/Meshtastic/Router/NavigationState.swift b/Meshtastic/Router/NavigationState.swift index 8c2ff6b3..48a97b93 100644 --- a/Meshtastic/Router/NavigationState.swift +++ b/Meshtastic/Router/NavigationState.swift @@ -52,7 +52,6 @@ enum SettingsNavigationState: String { case debugLogs case appFiles case firmwareUpdates - case tools } struct NavigationState: Hashable { diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index 25b0e75a..d3d15a66 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -355,13 +355,6 @@ struct Settings: View { Image(systemName: "gearshape") } } - NavigationLink(value: SettingsNavigationState.tools) { - Label { - Text("Tools") - } icon: { - Image(systemName: "hammer") - } - } NavigationLink(value: SettingsNavigationState.routes) { Label { Text("Routes") @@ -528,8 +521,6 @@ struct Settings: View { AppData() case .firmwareUpdates: Firmware(node: node) - case .tools: - Tools() } } .onChange(of: UserDefaults.preferredPeripheralNum ) { _, newConnectedNode in diff --git a/Meshtastic/Views/Settings/Tools.swift b/Meshtastic/Views/Settings/Tools.swift deleted file mode 100644 index 75e439de..00000000 --- a/Meshtastic/Views/Settings/Tools.swift +++ /dev/null @@ -1,164 +0,0 @@ -// -// Tools.swift -// Meshtastic -// -// Created by Benjamin Faershtein on 12/31/25. -// - -import SwiftUI -import CoreNFC -import MeshtasticProtobufs -import OSLog - -struct Tools: View { - @EnvironmentObject var accessoryManager: AccessoryManager - @Environment(\.managedObjectContext) var context - - @StateObject private var nfcReader = NFCReader() - - var connectedNode: NodeInfoEntity? { - if let num = accessoryManager.activeDeviceNum { - return getNodeInfo(id: num, context: context) - } - return nil - } - - var qrString: String { - var contact = SharedContact() - contact.nodeNum = UInt32(connectedNode?.num ?? 0) - contact.user = connectedNode?.toProto().user ?? User() - contact.manuallyVerified = true - - do { - let contactString = try contact.serializedData().base64EncodedString() - return "https://meshtastic.org/v/#" + contactString.base64ToBase64url() - } catch { - Logger.services.error("Error serializing contact: \(error)") - return "" - } - } - - var body: some View { - VStack{ - List { - Section(header: Text("Create Node Contact NFC Tag")) { - if let node = connectedNode { - Text("Node Name: \(node.user?.longName ?? "Unknown")") - - Button { - nfcReader.scan(theActualData: qrString) - } label: { - Label("Write Contact to NFC Tag", systemImage: "tag") - } - .disabled(qrString.isEmpty) - } - } - } - } - .navigationTitle("Tools") - .navigationBarTitleDisplayMode(.inline) - } -} - -#Preview { - Tools() -} - -final class NFCReader: NSObject, ObservableObject, NFCNDEFReaderSessionDelegate { - - private let logger = Logger(subsystem: "org.meshtastic.app", category: "NFC") - private var payloadString = "" - private var session: NFCNDEFReaderSession? - - func scan(theActualData: String) { - payloadString = theActualData - - session = NFCNDEFReaderSession( - delegate: self, - queue: nil, - invalidateAfterFirstRead: false - ) - - session?.alertMessage = "Hold your iPhone near the NFC tag." - session?.begin() - } - - func readerSessionDidBecomeActive(_ session: NFCNDEFReaderSession) { - logger.debug("NFC session became active") - } - - func readerSession(_ session: NFCNDEFReaderSession, - didInvalidateWithError error: Error) { - logger.error("NFC session invalidated: \(error.localizedDescription)") - } - - func readerSession(_ session: NFCNDEFReaderSession, - didDetectNDEFs messages: [NFCNDEFMessage]) { - } - - func readerSession(_ session: NFCNDEFReaderSession, - didDetect tags: [NFCNDEFTag]) { - - guard tags.count == 1, let tag = tags.first else { - session.alertMessage = "More than one tag detected. Please present only one." - DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(500)) { - session.restartPolling() - } - return - } - - session.connect(to: tag) { error in - if let error { - self.logger.error("Failed to connect to tag: \(error.localizedDescription)") - session.alertMessage = "Failed to connect to tag." - session.invalidate() - return - } - - tag.queryNDEFStatus { status, _, error in - if let error { - self.logger.error("Failed to query NDEF status: \(error.localizedDescription)") - session.alertMessage = "Failed to read tag." - session.invalidate() - return - } - - switch status { - case .notSupported: - self.logger.error("Tag does not support NDEF") - session.alertMessage = "Tag does not support NDEF." - session.invalidate() - - case .readOnly: - self.logger.error("Tag is read-only") - session.alertMessage = "Tag is read-only." - session.invalidate() - - case .readWrite: - guard let payload = - NFCNDEFPayload.wellKnownTypeURIPayload( - string: self.payloadString - ) else { - self.logger.error("Invalid NDEF payload") - session.alertMessage = "Invalid payload." - session.invalidate() - return - } - - let message = NFCNDEFMessage(records: [payload]) - - tag.writeNDEF(message) { error in - if let error { - self.logger.error("Failed to write NDEF: \(error.localizedDescription)") - session.alertMessage = "Failed to write tag." - } else { - self.logger.info("Successfully wrote NFC tag") - session.alertMessage = "NFC tag written successfully." - } - session.invalidate() - } - } - } - } - } -}