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 <garth@meshtastic.com>
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 e0f0b4a0f7 (ChannelMessageList and UserMessageList: scroll to bottom onFirstAppear)

* Revert "ChannelMessageList and UserMessageList: debouncedScrollToBottom; keyboardWillShowNotification/keyboardDidShowNotification"

This reverts commit ee1a7c4415.

---------

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 <jake-b@users.noreply.github.com>
Co-authored-by: Mike Robbins <mrobbins@alum.mit.edu>

* 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 <martinbogo@gmail.com>
Co-authored-by: Jake-B <jake-b@users.noreply.github.com>

* 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 <garth@meshtastic.com>

* 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 <jake-b@users.noreply.github.com>

* 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 <jbennett@incomsystems.biz>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>

* 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 <jbennett@incomsystems.biz>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>

* 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 <jbennett@incomsystems.biz>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
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 <jbennett@incomsystems.biz>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>

* 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 <jbennett@incomsystems.biz>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>

* 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 <jake-b@users.noreply.github.com>
Co-authored-by: Mike Robbins <mrobbins@alum.mit.edu>
Co-authored-by: Martin Bogomolni <martinbogo@gmail.com>
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 <jbennett@incomsystems.biz>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
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 <dubsectordevelopment@gmail.com>
This commit is contained in:
Garth Vander Houwen 2025-12-21 12:15:01 -08:00 committed by GitHub
parent 09c31e8228
commit b30dc8645a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
96 changed files with 3369 additions and 4282 deletions

View file

@ -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"
}
}

View file

@ -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 = "<group>"; };
2346A7182E2FB9A300CB9239 /* SerialConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialConnection.swift; sourceTree = "<group>"; };
2346A71C2E2FB9C500CB9239 /* SerialTransport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialTransport.swift; sourceTree = "<group>"; };
2349A0492EAE4DA30060A581 /* ManualConnectionList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManualConnectionList.swift; sourceTree = "<group>"; };
2373AE122D0A216C0086C749 /* MetricsChartSeries.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetricsChartSeries.swift; sourceTree = "<group>"; };
2373AE142D0A24930086C749 /* MetricsSeriesList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetricsSeriesList.swift; sourceTree = "<group>"; };
2373AE162D0A26620086C749 /* EnvironmentDefaultSeries.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentDefaultSeries.swift; sourceTree = "<group>"; };
@ -407,6 +410,7 @@
B3E905B02B71F7F300654D07 /* TextMessageField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextMessageField.swift; sourceTree = "<group>"; };
BC10380E2DD4333C00B00BFA /* AddContactIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactIntent.swift; sourceTree = "<group>"; };
BC6B45FE2CB2F98900723CEB /* SaveChannelSettingsIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveChannelSettingsIntent.swift; sourceTree = "<group>"; };
BCA9A82B2EC802CF00166292 /* CompassView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompassView.swift; sourceTree = "<group>"; };
BCB35B4E2E5FC41E00B04F60 /* MessageNodeIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageNodeIntent.swift; sourceTree = "<group>"; };
BCB613802C67290800485544 /* SendWaypointIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendWaypointIntent.swift; sourceTree = "<group>"; };
BCB613822C672A2600485544 /* MessageChannelIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageChannelIntent.swift; sourceTree = "<group>"; };
@ -832,6 +836,7 @@
23D316922E5618D2002FA4FB /* AsyncGate.swift */,
23E23F912E392C2B00919073 /* LogRecord+StringRepresentation.swift */,
23D9D9382E50DA97005D1C18 /* ResettableTimer.swift */,
2349A0492EAE4DA30060A581 /* ManualConnectionList.swift */,
);
path = Helpers;
sourceTree = "<group>";
@ -1271,6 +1276,7 @@
8D3F8A3E2D44BB02009EAAA4 /* PowerMetrics.swift */,
237B46952DC8F1C100B22D99 /* RateLimitedButton.swift */,
23A1AFB62E42BD2500E46C96 /* RXTXIndicatorView.swift */,
BCA9A82B2EC802CF00166292 /* CompassView.swift */,
);
path = Helpers;
sourceTree = "<group>";
@ -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" */ = {

View file

@ -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"
}
}
],

View file

@ -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
}

View file

@ -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)
}
}
}
}

View file

@ -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)")
}
}

View file

@ -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)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
meshPacket.channel = 0
var dataMessage = DataMessage()
guard let adminData: Data = try? adminPacket.serializedData() else {
throw AccessoryError.ioFailed("sendReboot: Unable to serialize Admin packet")
if !addChannels {
// 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)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
meshPacket.channel = 0
var dataMessage = DataMessage()
guard let adminData: Data = try? adminPacket.serializedData() else {
throw AccessoryError.ioFailed("sendReboot: Unable to serialize Admin packet")
}
dataMessage.payload = adminData
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
var toRadio: ToRadio!
toRadio = ToRadio()
toRadio.packet = meshPacket
let logString = String.localizedStringWithFormat("Sent a LoRa.Config for: %@".localized, String(deviceNum))
try await send(toRadio, debugDescription: logString)
}
dataMessage.payload = adminData
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
var toRadio: ToRadio!
toRadio = ToRadio()
toRadio.packet = meshPacket
let logString = String.localizedStringWithFormat("Sent a LoRa.Config for: %@".localized, String(deviceNum))
try await send(toRadio, debugDescription: logString)
Logger.transport.debug("[AccessoryManager] sending wantConfig for saveChannelSet")
try await sendWantConfig()
}
@ -1813,7 +1814,7 @@ extension AccessoryManager {
public func sendNodeDBReset(fromUser: UserEntity, toUser: UserEntity) async throws {
var adminPacket = AdminMessage()
adminPacket.nodedbReset = 5
adminPacket.nodedbReset = true
if fromUser != toUser {
adminPacket.sessionPasskey = toUser.userNode?.sessionPasskey ?? Data()
}

View file

@ -141,7 +141,7 @@ class AccessoryManager: ObservableObject, MqttClientProxyManagerDelegate {
let transports: [any Transport]
// Config
public var wantRangeTestPackets = true
public var wantRangeTestPackets = false
var wantStoreAndForwardPackets = false
var shouldAutomaticallyConnectToPreferredPeripheral = true
@ -302,9 +302,9 @@ class AccessoryManager: ObservableObject, MqttClientProxyManagerDelegate {
Logger.transport.error("updateDevice<T> 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()

View file

@ -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<T>(deviceId: UUID, key: WritableKeyPath<Device, T>, 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
}
}

View file

@ -31,7 +31,7 @@ enum ConnectionEvent {
case disconnected(shouldReconnect: Bool)
}
enum ConnectionState: Equatable {
enum ConnectionState: Equatable, Codable {
case disconnected
case connecting
case connected

View file

@ -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))"
}
}
}

View file

@ -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))

View file

@ -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")
}

View file

@ -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")
}
}

View file

@ -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 (065535)
return port >= 0 && port <= 65535
}
}
extension NetService {
var ipv4Address: String? {
for addressData in addresses ?? [] {

View file

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "heltec_v4.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 36 KiB

View file

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "muzi_r1_neo.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,141 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 217.61 761.78589"
version="1.1"
id="svg15"
sodipodi:docname="buyer benmeshtastic-01 (6).svg"
width="217.61"
height="761.78589"
inkscape:version="1.4 (e7c3feb1, 2024-10-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview15"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="1.0249904"
inkscape:cx="-4.3902849"
inkscape:cy="683.42102"
inkscape:window-width="1472"
inkscape:window-height="890"
inkscape:window-x="0"
inkscape:window-y="38"
inkscape:window-maximized="1"
inkscape:current-layer="svg15" />
<defs
id="defs1">
<style
id="style1">.cls-1{fill:#9a4451;}.cls-2{fill:#5b232b;}.cls-3{fill:#eadadb;}.cls-4{fill:#191919;}.cls-5{fill:#1f2020;}.cls-6{fill:#353535;}.cls-7{fill:#8b3944;}.cls-8{fill:#2a2a2a;}.cls-9{fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:0.95px;}</style>
</defs>
<g
id="Layer_3"
data-name="Layer 3"
transform="translate(-407.715,-82.379135)">
<path
class="cls-1"
d="m 617.2,633.34 v 21.18 a 5.74,5.74 0 0 1 -11.48,0 v -14.38 l -50,26.45 -1,2.9 1,1.54 0.08,137.28 -0.05,2.08 69.19,-13 V 628.81 Z"
id="path1" />
<path
class="cls-2"
d="m 624.85,628.49 v 0.32 l -7.65,4.53 v -1.79 a 5.74,5.74 0 0 0 -11.48,0 v 8.59 l -47.72,28.13 -90.13,48.82 -13.82,92.52 -17.26,0.38 -28.6,-8.44 v -85.47 a 9.33,9.33 0 0 1 1,-4.12 l 19.64,-40 a 8.71,8.71 0 0 1 0.85,-1.4 l 5.13,-7 0.11,-0.82 11,-14.53 v 0 l 5.36,-7.05 h -0.11 l 3.37,-4.6 20.46,-28.15 a 9.35,9.35 0 0 1 5,-3.51 l 5.46,7.23 a 4.65,4.65 0 0 0 3.69,1.83 h 32.07 a 4.62,4.62 0 0 0 3.6,-1.72 l 6.19,-7.68 h 69.87 a 23.93,23.93 0 0 1 23.97,23.93 z"
id="path2" />
<path
class="cls-2"
d="m 451.29,641.14 -5.36,7.05 v 0 c -0.13,-0.13 0.33,-0.73 0.33,-0.73 l 5,-6.32 z"
id="path3" />
<path
class="cls-2"
d="m 531.05,604.56 -6.19,7.68 a 4.62,4.62 0 0 1 -3.6,1.72 h -32.07 a 4.65,4.65 0 0 1 -3.69,-1.83 L 480,604.9 a 9.41,9.41 0 0 1 2.49,-0.34 z"
id="path4" />
<path
class="cls-3"
d="m 531.05,604.56 -6.19,7.68 a 4.62,4.62 0 0 1 -3.6,1.72 h -32.07 a 4.65,4.65 0 0 1 -3.69,-1.83 L 480,604.9 a 9.41,9.41 0 0 1 2.49,-0.34 z"
id="path5" />
<path
class="cls-4"
d="m 462.31,618.89 v 7 l -12.15,16.71 -11.08,15.2 -10.29,14.2 v -48.63 l 5.51,-37.49 a 2.21,2.21 0 0 1 2.18,-1.88 h 17.73 a 2.21,2.21 0 0 1 2.17,1.81 z"
id="path6" />
<polygon
class="cls-5"
points="436.48,584 436.48,125.69 454.02,125.58 454.02,584 "
id="polygon6" />
<path
class="cls-4"
d="m 456.2,92.91 c 0,0.12 0,0.23 0,0.35 L 455.05,125 a 0.82,0.82 0 0 1 -0.82,0.78 h -18.14 a 0.81,0.81 0 0 1 -0.81,-0.79 l -1,-31.68 a 9.78,9.78 0 0 1 5.05,-8.88 h 0.08 a 12.08,12.08 0 0 1 11.77,0 9.78,9.78 0 0 1 5.02,8.48 z"
id="path7" />
<polygon
class="cls-6"
points="439.08,657.8 439.08,584 450.16,584 450.16,642.56 "
id="polygon7" />
<path
class="cls-6"
d="m 448.65,93 v 32.7 h -6.37 V 93 a 3.19,3.19 0 0 1 3.19,-3.19 3.15,3.15 0 0 1 2.25,0.94 3.18,3.18 0 0 1 0.93,2.25 z"
id="path8" />
<path
class="cls-7"
d="M 555.75,675.9 V 843.69 H 439.42 V 677 a 27.34,27.34 0 0 1 27.34,-27.34 h 62.75 a 26.24,26.24 0 0 1 26.24,26.24 z"
id="path9" />
<path
class="cls-8"
d="m 624.85,797.42 v 22.34 a 23.93,23.93 0 0 1 -23.93,23.93 h -168.8 a 23.93,23.93 0 0 1 -23.93,-23.93 v -18.21 l 31.25,8.45 h 118.62 z"
id="path10" />
</g>
<g
id="Layer_2"
data-name="Layer 2"
transform="translate(-407.715,-82.379135)">
<path
class="cls-9"
d="m 624.85,628.49 v 191.27 a 23.94,23.94 0 0 1 -23.93,23.93 H 432.12 A 23.93,23.93 0 0 1 408.19,819.76 V 716.08 a 9.46,9.46 0 0 1 0.95,-4.12 l 19.65,-40 a 8.38,8.38 0 0 1 0.85,-1.41 l 24.91,-34 20.45,-28.14 a 9.35,9.35 0 0 1 7.56,-3.85 h 118.36 a 23.93,23.93 0 0 1 23.93,23.93 z"
id="path11" />
<rect
class="cls-9"
x="605.71997"
y="625.81"
width="11.49"
height="34.459999"
rx="5.7399998"
id="rect11" />
<polyline
class="cls-9"
points="408.19 801.55 439.44 809.97 439.44 809.97 555.79 810.09 555.79 810.09 624.85 797.42"
id="polyline11" />
<path
class="cls-9"
d="m 480,604.9 5.46,7.23 a 4.65,4.65 0 0 0 3.69,1.83 h 32.07 a 4.59,4.59 0 0 0 3.59,-1.72 l 6.2,-7.68"
id="path12" />
<path
class="cls-9"
d="m 428.79,672 v -48.63 l 5.51,-37.49 a 2.21,2.21 0 0 1 2.18,-1.88 h 17.73 a 2.19,2.19 0 0 1 2.16,1.81 l 5.94,33.08 v 7"
id="path13" />
<line
class="cls-9"
x1="436.48001"
y1="584"
x2="436.48001"
y2="125.69"
id="line13" />
<line
class="cls-9"
x1="454.01999"
y1="584"
x2="454.01999"
y2="125.58"
id="line14" />
<path
class="cls-9"
d="m 454.23,125.73 h -18.14 a 0.82,0.82 0 0 1 -0.82,-0.79 l -1,-31.69 a 9.78,9.78 0 0 1 5,-8.87 h 0.09 a 12.11,12.11 0 0 1 11.77,0 v 0 a 9.81,9.81 0 0 1 5,8.91 l -1.14,31.68 a 0.82,0.82 0 0 1 -0.76,0.76 z"
id="path14" />
<path
class="cls-9"
d="M 555.75,843.69 V 675.9 A 26.24,26.24 0 0 0 529.51,649.66 H 466.76 A 27.34,27.34 0 0 0 439.42,677 v 166.69"
id="path15" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.7 KiB

View file

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "thinknode_m3.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.9 KiB

View file

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "thinknode_m4.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 95 KiB

View file

@ -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:

View file

@ -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
}
}

View file

@ -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<MessageEntity> {
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
}
}

View file

@ -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> = 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
}
}

View file

@ -6,23 +6,39 @@
//
import Foundation
import CoreData
extension MyInfoEntity {
var messagePredicate: NSPredicate {
return NSPredicate(format: "toUser == nil AND isEmoji == false")
}
var messageFetchRequest: NSFetchRequest<MessageEntity> {
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

View file

@ -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() }
}

View file

@ -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<MessageEntity> {
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

View file

@ -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)

View file

@ -6,6 +6,7 @@
//
import Foundation
import OSLog
@propertyWrapper
struct UserDefault<T: Decodable> {
@ -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 {

View file

@ -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")

View file

@ -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)

View file

@ -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")
}
}

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="24299" systemVersion="25A354" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="24299" systemVersion="25A362" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="AmbientLightingConfigEntity" representedClassName="AmbientLightingConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="blue" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="current" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
@ -164,6 +164,8 @@
<attribute name="read" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="realACK" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="receivedACK" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="relayNode" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="relays" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="replyID" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>

View file

@ -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)
}
}

View file

@ -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

View file

@ -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"
]
}
]

View file

@ -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)
}
}
}
}
}

View file

@ -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
)
}
}

View file

@ -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)
}
)
}
}

View file

@ -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))

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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")

View file

@ -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])

View file

@ -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

View file

@ -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<MessageEntity>
init(user: UserEntity) {
self.user = user
// Configure fetch request here
let request: NSFetchRequest<MessageEntity> = 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

View file

@ -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)

View file

@ -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 {

View file

@ -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 {
}
}
}

View file

@ -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<RouteEntity>
@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
}

View file

@ -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!
}
}

View file

@ -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")
}

View file

@ -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<Content: View>: 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<Content>, rhs: NodeMapContentEquatableWrapper<Content>) -> 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 {

View file

@ -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))
}
}

View file

@ -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)
}

View file

@ -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())

View file

@ -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

View file

@ -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> = NodeInfoEntity.fetchRequest()
@ -154,7 +154,7 @@ fileprivate struct FilteredNodeList: View {
@EnvironmentObject var accessoryManager: AccessoryManager
@FetchRequest private var nodes: FetchedResults<NodeInfoEntity>
@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

View file

@ -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()

View file

@ -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) {

View file

@ -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")

View file

@ -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 ""
}
}
}

View file

@ -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 {

View file

@ -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 }

View file

@ -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 }

View file

@ -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)

View file

@ -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<UpdateInterval>) {
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)
}
}
}

View file

@ -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(

View file

@ -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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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")
}

View file

@ -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<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {

View file

@ -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
/// <status battery='100' />
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
/// <contact endpoint='0.0.0.0:4242:tcp' phone='+12345678' callsign='FALKE'/>
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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {

View file

@ -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<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {

View file

@ -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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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")
}

View file

@ -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<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {

File diff suppressed because it is too large Load diff

View file

@ -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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {

View file

@ -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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {

View file

@ -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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {

View file

@ -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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {

View file

@ -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() {}

File diff suppressed because it is too large Load diff

View file

@ -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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {

View file

@ -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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {

View file

@ -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<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {

View file

@ -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")
}

View file

@ -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<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let _ = try decoder.nextFieldNumber() {
}
// Load everything into unknown fields
while try decoder.nextFieldNumber() != nil {}
}
public func traverse<V: SwiftProtobuf.Visitor>(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<D: SwiftProtobuf.Decoder>(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")
}

View file

@ -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<D: SwiftProtobuf.Decoder>(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")
}

View file

@ -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<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {

View file

@ -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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {

View file

@ -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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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<D: SwiftProtobuf.Decoder>(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)

View file

@ -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<D: SwiftProtobuf.Decoder>(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")
}

@ -1 +1 @@
Subproject commit c1e31a9655e9920a8b5b8eccdf7c69ef1ae42a49
Subproject commit 62ef17b3d1625fc6d78ed661f614d0baad4be9ef