mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Merge branch '2.7.8' into tak-server
This commit is contained in:
commit
be971c2d2d
25 changed files with 654 additions and 151 deletions
|
|
@ -13742,6 +13742,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Exchange User Info" : {
|
||||
|
||||
},
|
||||
"Exclamation" : {
|
||||
"localizations" : {
|
||||
|
|
@ -14222,6 +14225,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Failed to exchange user info." : {
|
||||
|
||||
},
|
||||
"Failed to get a valid position to exchange" : {
|
||||
"localizations" : {
|
||||
|
|
@ -35632,6 +35638,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tap to enter emoji" : {
|
||||
|
||||
},
|
||||
"Tapback" : {
|
||||
"localizations" : {
|
||||
|
|
@ -40315,6 +40324,12 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"User Info Exchange Failed" : {
|
||||
|
||||
},
|
||||
"User Info Sent" : {
|
||||
|
||||
},
|
||||
"User Privacy" : {
|
||||
|
||||
|
|
@ -42406,7 +42421,10 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Your user info has been sent with a request for a response with their user info." : {
|
||||
|
||||
}
|
||||
},
|
||||
"version" : "1.1"
|
||||
}
|
||||
}
|
||||
|
|
@ -171,6 +171,7 @@
|
|||
DD3CC6C028E7A60700FA9159 /* MessagingEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3CC6BF28E7A60700FA9159 /* MessagingEnums.swift */; };
|
||||
DD3CC6C228EB9D4900FA9159 /* UpdateCoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3CC6C128EB9D4900FA9159 /* UpdateCoreData.swift */; };
|
||||
DD3D17E02C3FB67200561584 /* LocalWeatherConditions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3D17DF2C3FB67200561584 /* LocalWeatherConditions.swift */; };
|
||||
DD4074692F1233F400BCC22F /* ExchangeUserInfoButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4074682F1233F400BCC22F /* ExchangeUserInfoButton.swift */; };
|
||||
DD41582628582E9B009B0E59 /* DeviceConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD41582528582E9B009B0E59 /* DeviceConfig.swift */; };
|
||||
DD415828285859C4009B0E59 /* TelemetryConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD415827285859C4009B0E59 /* TelemetryConfig.swift */; };
|
||||
DD41582A28585C32009B0E59 /* RangeTestConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD41582928585C32009B0E59 /* RangeTestConfig.swift */; };
|
||||
|
|
@ -213,7 +214,6 @@
|
|||
DD93800B2BA3F968008BEC06 /* NodeMapContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD93800A2BA3F968008BEC06 /* NodeMapContent.swift */; };
|
||||
DD93800E2BA74D0C008BEC06 /* ChannelForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD93800D2BA74D0C008BEC06 /* ChannelForm.swift */; };
|
||||
DD94B7402ACCE3BE00DCD1D1 /* MapSettingsForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD94B73F2ACCE3BE00DCD1D1 /* MapSettingsForm.swift */; };
|
||||
DD964FBD296E6B01007C176F /* EmojiOnlyTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD964FBC296E6B01007C176F /* EmojiOnlyTextField.swift */; };
|
||||
DD964FC2297272AE007C176F /* WaypointEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD964FC1297272AE007C176F /* WaypointEntityExtension.swift */; };
|
||||
DD964FC62975DBFD007C176F /* QueryCoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD964FC52975DBFD007C176F /* QueryCoreData.swift */; };
|
||||
DD97E96628EFD9820056DDA4 /* MeshtasticLogo.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD97E96528EFD9820056DDA4 /* MeshtasticLogo.swift */; };
|
||||
|
|
@ -227,6 +227,7 @@
|
|||
DD9C70112E916EBD00106227 /* UpdateIntervalPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD9C70102E916EA200106227 /* UpdateIntervalPicker.swift */; };
|
||||
DDA0B6B2294CDC55001356EC /* Channels.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDA0B6B1294CDC55001356EC /* Channels.swift */; };
|
||||
DDA1C48E28DB49D3009933EC /* ChannelRoles.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDA1C48D28DB49D3009933EC /* ChannelRoles.swift */; };
|
||||
DDA3DFDA2F10B39600D8F103 /* UIKeyboardType.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDA3DFD92F10B39600D8F103 /* UIKeyboardType.swift */; };
|
||||
DDA6B2E928419CF2003E8C16 /* MeshPackets.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDA6B2E828419CF2003E8C16 /* MeshPackets.swift */; };
|
||||
DDA9515A2BC6624100CEA535 /* TelemetryWeather.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDA951592BC6624100CEA535 /* TelemetryWeather.swift */; };
|
||||
DDA9515C2BC6631200CEA535 /* TelemetryEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDA9515B2BC6631200CEA535 /* TelemetryEnums.swift */; };
|
||||
|
|
@ -518,6 +519,7 @@
|
|||
DD3CC6C128EB9D4900FA9159 /* UpdateCoreData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateCoreData.swift; sourceTree = "<group>"; };
|
||||
DD3D17DC2C3D7B1400561584 /* MeshtasticDataModelV 39.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 39.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DD3D17DF2C3FB67200561584 /* LocalWeatherConditions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalWeatherConditions.swift; sourceTree = "<group>"; };
|
||||
DD4074682F1233F400BCC22F /* ExchangeUserInfoButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExchangeUserInfoButton.swift; sourceTree = "<group>"; };
|
||||
DD41582528582E9B009B0E59 /* DeviceConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceConfig.swift; sourceTree = "<group>"; };
|
||||
DD415827285859C4009B0E59 /* TelemetryConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryConfig.swift; sourceTree = "<group>"; };
|
||||
DD41582928585C32009B0E59 /* RangeTestConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RangeTestConfig.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -573,7 +575,6 @@
|
|||
DD93800A2BA3F968008BEC06 /* NodeMapContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeMapContent.swift; sourceTree = "<group>"; };
|
||||
DD93800D2BA74D0C008BEC06 /* ChannelForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelForm.swift; sourceTree = "<group>"; };
|
||||
DD94B73F2ACCE3BE00DCD1D1 /* MapSettingsForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapSettingsForm.swift; sourceTree = "<group>"; };
|
||||
DD964FBC296E6B01007C176F /* EmojiOnlyTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiOnlyTextField.swift; sourceTree = "<group>"; };
|
||||
DD964FC029724F6D007C176F /* MeshtasticDataModelV6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV6.xcdatamodel; sourceTree = "<group>"; };
|
||||
DD964FC1297272AE007C176F /* WaypointEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaypointEntityExtension.swift; sourceTree = "<group>"; };
|
||||
DD964FC52975DBFD007C176F /* QueryCoreData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueryCoreData.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -591,6 +592,7 @@
|
|||
DDA0B6B1294CDC55001356EC /* Channels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Channels.swift; sourceTree = "<group>"; };
|
||||
DDA1C48D28DB49D3009933EC /* ChannelRoles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelRoles.swift; sourceTree = "<group>"; };
|
||||
DDA28B1B2D32C89200EF726F /* MeshtasticDataModelV 48.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 48.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DDA3DFD92F10B39600D8F103 /* UIKeyboardType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKeyboardType.swift; sourceTree = "<group>"; };
|
||||
DDA6B2E828419CF2003E8C16 /* MeshPackets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshPackets.swift; sourceTree = "<group>"; };
|
||||
DDA951592BC6624100CEA535 /* TelemetryWeather.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TelemetryWeather.swift; sourceTree = "<group>"; };
|
||||
DDA9515B2BC6631200CEA535 /* TelemetryEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryEnums.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -883,6 +885,7 @@
|
|||
251926882C3BAF2E00249DF5 /* Actions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DD4074682F1233F400BCC22F /* ExchangeUserInfoButton.swift */,
|
||||
DDDFE73E2D0D48FF0044463C /* IgnoreNodeButton.swift */,
|
||||
251926842C3BA97800249DF5 /* FavoriteNodeButton.swift */,
|
||||
251926892C3BB1B200249DF5 /* ExchangePositionsButton.swift */,
|
||||
|
|
@ -1351,7 +1354,6 @@
|
|||
DD1BEF492E0292220090CE24 /* KeychainHelper.swift */,
|
||||
DD913638270DFF4C00D7ACF3 /* LocalNotificationManager.swift */,
|
||||
DDA6B2E828419CF2003E8C16 /* MeshPackets.swift */,
|
||||
DD964FBC296E6B01007C176F /* EmojiOnlyTextField.swift */,
|
||||
DD3619142B1EF9F900C41C8C /* LocationsHandler.swift */,
|
||||
6D825E612C34786C008DBEE4 /* CommonRegex.swift */,
|
||||
3D3417B32E2730EC006A988B /* GeoJSONOverlayManager.swift */,
|
||||
|
|
@ -1432,6 +1434,7 @@
|
|||
DDDB444729F8A9C900EE2349 /* String.swift */,
|
||||
DD77093E2AA1B146007A8BF0 /* UIColor.swift */,
|
||||
DDDB444F29F8AC9C00EE2349 /* UIImage.swift */,
|
||||
DDA3DFD92F10B39600D8F103 /* UIKeyboardType.swift */,
|
||||
DDDB443F29F79AB000EE2349 /* UserDefaults.swift */,
|
||||
DDB75A0E2A05920E006ED576 /* FileManager.swift */,
|
||||
DDB75A102A059258006ED576 /* Url.swift */,
|
||||
|
|
@ -1730,7 +1733,6 @@
|
|||
DD77093B2AA1ABB8007A8BF0 /* BluetoothTips.swift in Sources */,
|
||||
D9C9839D2B79CFD700BDBE6A /* TextMessageSize.swift in Sources */,
|
||||
DDC94FCE29CF55310082EA6E /* RtttlConfig.swift in Sources */,
|
||||
DD964FBD296E6B01007C176F /* EmojiOnlyTextField.swift in Sources */,
|
||||
DD1BEF4E2E03916A0090CE24 /* ChannelsHelp.swift in Sources */,
|
||||
DD8169FF272476C700F4AB02 /* LogDocument.swift in Sources */,
|
||||
BCD93CBA2D9E11A2006C9214 /* DisconnectNodeIntent.swift in Sources */,
|
||||
|
|
@ -1783,6 +1785,7 @@
|
|||
DDDB444A29F8AA3A00EE2349 /* CLLocationCoordinate2D.swift in Sources */,
|
||||
25C49D902C471AEA0024FBD1 /* Constants.swift in Sources */,
|
||||
ABB99DEB2E2EA1C500CFBD05 /* AppIconPicker.swift in Sources */,
|
||||
DD4074692F1233F400BCC22F /* ExchangeUserInfoButton.swift in Sources */,
|
||||
DD41582628582E9B009B0E59 /* DeviceConfig.swift in Sources */,
|
||||
DDF45C372BC46A5A005ED5F2 /* TimeZone.swift in Sources */,
|
||||
DD007BAE2AA4E91200F5FA12 /* MyInfoEntityExtension.swift in Sources */,
|
||||
|
|
@ -1863,6 +1866,7 @@
|
|||
DDA6B2E928419CF2003E8C16 /* MeshPackets.swift in Sources */,
|
||||
DDCE4E2C2869F92900BE9F8F /* UserConfig.swift in Sources */,
|
||||
BCB613852C68703800485544 /* NodePositionIntent.swift in Sources */,
|
||||
DDA3DFDA2F10B39600D8F103 /* UIKeyboardType.swift in Sources */,
|
||||
DDB75A212A12B954006ED576 /* LoRaSignalStrength.swift in Sources */,
|
||||
DD6193752862F6E600E59241 /* ExternalNotificationConfig.swift in Sources */,
|
||||
DD268D8E2BCC90E2008073AE /* RouteEnums.swift in Sources */,
|
||||
|
|
@ -2177,7 +2181,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 14.6;
|
||||
MARKETING_VERSION = 2.7.7;
|
||||
MARKETING_VERSION = 2.7.8;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -2212,7 +2216,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 14.6;
|
||||
MARKETING_VERSION = 2.7.7;
|
||||
MARKETING_VERSION = 2.7.8;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -2244,7 +2248,7 @@
|
|||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 14.6;
|
||||
MARKETING_VERSION = 2.7.7;
|
||||
MARKETING_VERSION = 2.7.8;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
@ -2277,7 +2281,7 @@
|
|||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 14.6;
|
||||
MARKETING_VERSION = 2.7.7;
|
||||
MARKETING_VERSION = 2.7.8;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ extension AccessoryManager {
|
|||
}
|
||||
|
||||
// Step 1: Setup the connection
|
||||
Step(timeout: .seconds(2)) { @MainActor _ in
|
||||
Step(timeout: .seconds(5)) { @MainActor _ in
|
||||
Logger.transport.info("🔗👟[Connect] Step 1: connection to \(device.id, privacy: .public)")
|
||||
do {
|
||||
let connection: Connection
|
||||
|
|
@ -352,7 +352,6 @@ actor SequentialSteps {
|
|||
return
|
||||
}
|
||||
isRunning = false
|
||||
return
|
||||
throw AccessoryError.tooManyRetries
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ extension AccessoryManager {
|
|||
let tasks = transports.map { transport in
|
||||
Task {
|
||||
Logger.transport.info("🔎 [Discovery] Discovery stream started for transport \(String(describing: transport.type), privacy: .public)")
|
||||
for await event in transport.discoverDevices() {
|
||||
for await event in await transport.discoverDevices() {
|
||||
continuation.yield(event)
|
||||
}
|
||||
Logger.transport.info("🔎 [Discovery] Discovery stream closed for transport \(String(describing: transport.type), privacy: .public)")
|
||||
|
|
|
|||
|
|
@ -2118,4 +2118,34 @@ extension AccessoryManager {
|
|||
|
||||
try await sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription)
|
||||
}
|
||||
|
||||
public func exchangeUserInfo(fromUser: UserEntity, toUser: UserEntity) async throws -> Int64 {
|
||||
|
||||
let userProto = fromUser.toProto()
|
||||
guard let userPayload: Data = try? userProto.serializedData() else {
|
||||
throw AccessoryError.ioFailed("exchangeUserInfo: Unable to serialize User protobuf")
|
||||
}
|
||||
|
||||
var dataMessage = DataMessage()
|
||||
dataMessage.payload = userPayload
|
||||
dataMessage.portnum = PortNum.nodeinfoApp
|
||||
dataMessage.wantResponse = true
|
||||
|
||||
var meshPacket: MeshPacket = MeshPacket()
|
||||
meshPacket.to = UInt32(toUser.num)
|
||||
meshPacket.from = UInt32(fromUser.num)
|
||||
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
|
||||
meshPacket.priority = MeshPacket.Priority.reliable
|
||||
meshPacket.wantAck = true
|
||||
meshPacket.channel = UInt32(toUser.userNode?.channel ?? 0)
|
||||
meshPacket.decoded = dataMessage
|
||||
|
||||
var toRadio: ToRadio = ToRadio()
|
||||
toRadio.packet = meshPacket
|
||||
|
||||
let logString = String.localizedStringWithFormat("Sent User Info Exchange request from %@ to %@".localized, fromUser.longName ?? "Unknown".localized, toUser.longName ?? "Unknown".localized)
|
||||
try await send(toRadio, debugDescription: logString)
|
||||
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ protocol Connection: Actor {
|
|||
var isConnected: Bool { get }
|
||||
func send(_ data: ToRadio) async throws
|
||||
func connect() async throws -> AsyncStream<ConnectionEvent>
|
||||
func disconnect(withError: Error?, shouldReconnect: Bool) throws
|
||||
func disconnect(withError: Error?, shouldReconnect: Bool) async throws
|
||||
func drainPendingPackets() async throws
|
||||
func startDrainPendingPackets() throws
|
||||
|
||||
|
|
|
|||
|
|
@ -42,10 +42,10 @@ enum DiscoveryEvent {
|
|||
|
||||
protocol Transport {
|
||||
var type: TransportType { get }
|
||||
var status: TransportStatus { get }
|
||||
var status: TransportStatus { get async }
|
||||
|
||||
// Discovers devices asynchronously. For ongoing scans (e.g., BLE), this can yield via AsyncStream.
|
||||
func discoverDevices() -> AsyncStream<DiscoveryEvent>
|
||||
func discoverDevices() async -> AsyncStream<DiscoveryEvent>
|
||||
|
||||
// Connects to a device and returns a Connection.
|
||||
func connect(to device: Device) async throws -> any Connection
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ actor BLEConnection: Connection {
|
|||
self.delegate.setConnection(self)
|
||||
}
|
||||
|
||||
func disconnect(withError error: Error? = nil, shouldReconnect: Bool) throws {
|
||||
func disconnect(withError error: Error? = nil, shouldReconnect: Bool) async throws {
|
||||
if peripheral.state == .connected {
|
||||
if let characteristic = FROMRADIO_characteristic {
|
||||
peripheral.setNotifyValue(false, for: characteristic)
|
||||
|
|
@ -82,7 +82,7 @@ actor BLEConnection: Connection {
|
|||
}
|
||||
}
|
||||
|
||||
transport?.connectionDidDisconnect(fromPeripheral: peripheral)
|
||||
await transport?.connectionDidDisconnect(fromPeripheral: peripheral)
|
||||
|
||||
central.cancelPeripheralConnection(peripheral)
|
||||
peripheral.delegate = nil
|
||||
|
|
@ -217,8 +217,8 @@ actor BLEConnection: Connection {
|
|||
self.connectContinuation = nil
|
||||
}
|
||||
|
||||
private func notifyTransportOfDisconnect() {
|
||||
transport?.connectionDidDisconnect(fromPeripheral: peripheral)
|
||||
private func notifyTransportOfDisconnect() async {
|
||||
await transport?.connectionDidDisconnect(fromPeripheral: peripheral)
|
||||
}
|
||||
|
||||
func startRSSITask() {
|
||||
|
|
@ -450,7 +450,7 @@ actor BLEConnection: Connection {
|
|||
}
|
||||
|
||||
// Inform the active connection that there was an error and it should disconnect
|
||||
try self.disconnect(withError: error, shouldReconnect: shouldReconnect)
|
||||
try await self.disconnect(withError: error, shouldReconnect: shouldReconnect)
|
||||
}
|
||||
|
||||
func appDidEnterBackground() {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import Foundation
|
|||
import SwiftUI
|
||||
import OSLog
|
||||
|
||||
class BLETransport: Transport {
|
||||
actor BLETransport: Transport {
|
||||
|
||||
let meshtasticServiceCBUUID = CBUUID(string: "0x6BA1B218-15A8-461F-9FA8-5DCAE273EAFD")
|
||||
private let kCentralRestoreID = "com.meshtastic.central"
|
||||
|
|
@ -31,7 +31,7 @@ class BLETransport: Transport {
|
|||
private var cleanupTask: Task<Void, Never>?
|
||||
|
||||
// Transport properties
|
||||
var supportsManualConnection: Bool = false
|
||||
let supportsManualConnection: Bool = false
|
||||
let requiresPeriodicHeartbeat = false
|
||||
|
||||
init() {
|
||||
|
|
@ -46,19 +46,24 @@ class BLETransport: Transport {
|
|||
self.delegate.setTransport(self)
|
||||
}
|
||||
|
||||
nonisolated func discoverDevices() -> AsyncStream<DiscoveryEvent> {
|
||||
private func setDiscoveredDeviceContinuation(_ cont: AsyncStream<DiscoveryEvent>.Continuation?) {
|
||||
self.discoveredDeviceContinuation = cont
|
||||
}
|
||||
|
||||
func discoverDevices() -> AsyncStream<DiscoveryEvent> {
|
||||
AsyncStream { cont in
|
||||
Task {
|
||||
self.discoveredDeviceContinuation = cont
|
||||
await self.setDiscoveredDeviceContinuation(cont)
|
||||
|
||||
// This gate is opened when the CBCentralManager is in poweredOn state.
|
||||
// Its probably open already, but just to be sure in case we get here too quickly.
|
||||
try await self.setupCompleteGate.wait()
|
||||
|
||||
if !restoreInProgress {
|
||||
if await !self.restoreInProgress {
|
||||
centralManager.scanForPeripherals(withServices: [meshtasticServiceCBUUID], options: [CBCentralManagerScanOptionAllowDuplicatesKey: true])
|
||||
|
||||
for alreadyDiscoveredPeripheral in self.discoveredPeripherals.values.map({$0.peripheral}) {
|
||||
let peripherals = await self.discoveredPeripherals.values.map({$0.peripheral})
|
||||
for alreadyDiscoveredPeripheral in peripherals {
|
||||
let device = Device(id: alreadyDiscoveredPeripheral.identifier,
|
||||
name: alreadyDiscoveredPeripheral.name ?? "Unknown",
|
||||
transportType: .ble,
|
||||
|
|
@ -66,11 +71,13 @@ class BLETransport: Transport {
|
|||
cont.yield(.deviceFound(device))
|
||||
}
|
||||
}
|
||||
setupCleanupTask()
|
||||
await setupCleanupTask()
|
||||
}
|
||||
cont.onTermination = { _ in
|
||||
Logger.transport.error("🛜 [BLE] Discovery event stream has been canecelled.")
|
||||
self.stopScanning()
|
||||
Task {
|
||||
await self.stopScanning()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -188,6 +195,12 @@ class BLETransport: Transport {
|
|||
}
|
||||
}
|
||||
|
||||
private func cancelConnectContinuation(for peripheral: CBPeripheral) {
|
||||
self.connectContinuation?.resume(throwing: CancellationError())
|
||||
self.connectContinuation = nil
|
||||
self.connectionDidDisconnect(fromPeripheral: peripheral)
|
||||
}
|
||||
|
||||
func connect(to device: Device) async throws -> any Connection {
|
||||
guard let peripheral = discoveredPeripherals[UUID(uuidString: device.identifier)!] else {
|
||||
throw AccessoryError.connectionFailed("Peripheral not found")
|
||||
|
|
@ -211,9 +224,9 @@ class BLETransport: Transport {
|
|||
self.activeConnection = newConnection
|
||||
return newConnection
|
||||
} onCancel: {
|
||||
self.connectContinuation?.resume(throwing: CancellationError())
|
||||
self.connectContinuation = nil
|
||||
self.connectionDidDisconnect(fromPeripheral: peripheral.peripheral)
|
||||
Task {
|
||||
await self.cancelConnectContinuation(for: peripheral.peripheral)
|
||||
}
|
||||
}
|
||||
Logger.transport.debug("🛜 [BLE] Connect complete.")
|
||||
return returnConnection
|
||||
|
|
@ -226,7 +239,7 @@ class BLETransport: Transport {
|
|||
Task {
|
||||
if await connection.peripheral.identifier == peripheral.identifier {
|
||||
try await connection.disconnect(withError: AccessoryError.disconnected("BLE connection lost"), shouldReconnect: true)
|
||||
self.connectionDidDisconnect(fromPeripheral: peripheral)
|
||||
await self.connectionDidDisconnect(fromPeripheral: peripheral)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -264,7 +277,7 @@ class BLETransport: Transport {
|
|||
Logger.transport.debug("🛜 [BLETransport] Error while connecting. Disconnecting the active connection.")
|
||||
Task {
|
||||
try? await activeConnection.disconnect(withError: error, shouldReconnect: shouldReconnect)
|
||||
self.connectionDidDisconnect(fromPeripheral: peripheral)
|
||||
await self.connectionDidDisconnect(fromPeripheral: peripheral)
|
||||
}
|
||||
} else {
|
||||
Logger.transport.error("🚨 [BLETransport] unhandled error. May be in an inconsistent state.")
|
||||
|
|
@ -372,15 +385,20 @@ class BLETransport: Transport {
|
|||
}
|
||||
|
||||
Logger.transport.error("🛜 [BLE] Restoring peripheral in connecting state. ✅ didConnect Received!")
|
||||
Task { @MainActor in
|
||||
// In this case we need a full reconnect, so do the wantConfig, wantDatabase, and versionCheck
|
||||
try? await AccessoryManager.shared.connect(to: device, withConnection: restoredConnection, wantConfig: true, wantDatabase: true, versionCheck: true)
|
||||
restoreInProgress = false
|
||||
let connectTask = Task { @MainActor in
|
||||
try await AccessoryManager.shared.connect(to: device, withConnection: restoredConnection, wantConfig: true, wantDatabase: true, versionCheck: true)
|
||||
}
|
||||
|
||||
do {
|
||||
try await connectTask.value
|
||||
} catch {
|
||||
Logger.transport.error("🛜 [BLE] Error connecting during state restoration: \(error, privacy: .public)")
|
||||
}
|
||||
self.restoreInProgress = false
|
||||
} catch {
|
||||
// We had a conneciton failure during restoration.
|
||||
// We had a connection failure during restoration.
|
||||
Logger.transport.error("🛜 [BLE] Error restoring peripheral in connecting state. \(error, privacy: .public)")
|
||||
restoreInProgress = false
|
||||
self.restoreInProgress = false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -388,22 +406,28 @@ class BLETransport: Transport {
|
|||
let restoredConnection = BLEConnection(peripheral: peripheral, central: central, transport: self)
|
||||
self.activeConnection = restoredConnection
|
||||
Logger.transport.error("🛜 [BLE] Peripheral Connection found and state is connected setting this connection as the activeConnection.")
|
||||
Task { @MainActor in
|
||||
let connectTask = Task { @MainActor in
|
||||
// In this case we need a full reconnect, so do the wantConfig, wantDatabase, and versionCheck
|
||||
try? await AccessoryManager.shared.connect(to: device, withConnection: restoredConnection, wantConfig: false, wantDatabase: false, versionCheck: false)
|
||||
restoreInProgress = false
|
||||
try await AccessoryManager.shared.connect(to: device, withConnection: restoredConnection, wantConfig: false, wantDatabase: false, versionCheck: false)
|
||||
}
|
||||
do {
|
||||
try await connectTask.value
|
||||
} catch {
|
||||
Logger.transport.error("🛜 [BLE] Error connecting during state restoration: \(error, privacy: .public)")
|
||||
}
|
||||
|
||||
self.restoreInProgress = false
|
||||
Logger.transport.error("🛜 [BLE] Connection state successfully restored in the background.")
|
||||
default:
|
||||
// Since we're not going to attempt to reconnect in then allow normal device discovery
|
||||
Logger.transport.error("🛜 [BLE] Unhandled state restoration for state: \(cbPeripheralStateDescription(peripheral.state), privacy: .public).")
|
||||
restoreInProgress = false
|
||||
self.restoreInProgress = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func device(forManualConnection: String) -> Device? {
|
||||
nonisolated func device(forManualConnection: String) -> Device? {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -438,33 +462,33 @@ class BLEDelegate: NSObject, CBCentralManagerDelegate {
|
|||
}
|
||||
|
||||
func centralManagerDidUpdateState(_ central: CBCentralManager) {
|
||||
transport?.handleCentralState(central.state, central: central)
|
||||
Task { await transport?.handleCentralState(central.state, central: central) }
|
||||
}
|
||||
|
||||
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
|
||||
transport?.didDiscover(peripheral: peripheral, rssi: RSSI)
|
||||
Task { await transport?.didDiscover(peripheral: peripheral, rssi: RSSI) }
|
||||
}
|
||||
|
||||
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
|
||||
transport?.handleDidConnect(peripheral: peripheral, central: central)
|
||||
Task { await transport?.handleDidConnect(peripheral: peripheral, central: central) }
|
||||
}
|
||||
|
||||
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
|
||||
transport?.handleDidFailToConnect(peripheral: peripheral, error: error)
|
||||
Task { await transport?.handleDidFailToConnect(peripheral: peripheral, error: error) }
|
||||
}
|
||||
|
||||
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
|
||||
if let error = error as? NSError {
|
||||
Logger.transport.error("🛜 [BLETransport] Error while disconnecting peripheral: \(peripheral.name ?? ""): \(error)")
|
||||
transport?.handlePeripheralDisconnectError(peripheral: peripheral, error: error)
|
||||
Task { await transport?.handlePeripheralDisconnectError(peripheral: peripheral, error: error) }
|
||||
} else {
|
||||
Logger.transport.error("🛜 [BLETransport] Did succesfully disconnect peripheral: \(peripheral.name ?? "")")
|
||||
transport?.handlePeripheralDisconnect(peripheral: peripheral)
|
||||
Task { await transport?.handlePeripheralDisconnect(peripheral: peripheral) }
|
||||
}
|
||||
}
|
||||
|
||||
func centralManager(_ central: CBCentralManager, willRestoreState dict: [String: Any]) {
|
||||
self.transport?.handleWillRestoreState(dict: dict, central: central)
|
||||
Task { await self.transport?.handleWillRestoreState(dict: dict, central: central) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
13
Meshtastic/Extensions/UIKeyboardType.swift
Normal file
13
Meshtastic/Extensions/UIKeyboardType.swift
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
//
|
||||
// UIKeyboard.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright(c) Garth Vander Houwen 1/7/26.
|
||||
//
|
||||
import UIKit
|
||||
|
||||
extension UIKeyboardType {
|
||||
static var emoji: UIKeyboardType {
|
||||
return UIKeyboardType(rawValue: 124) ?? .default
|
||||
}
|
||||
}
|
||||
|
|
@ -39,7 +39,7 @@ struct EmojiOnlyTextField: UIViewRepresentable {
|
|||
@Binding var text: String
|
||||
var placeholder: String = ""
|
||||
var onBecomeFirstResponder: (() -> Void)?
|
||||
var onKeyboardTypeChanged: ((Bool) -> Void)? // true if emoji, false otherwise
|
||||
var onKeyboardTypeChanged: ((Bool) -> Void)? // true if NOT emoji (should dismiss), false if emoji
|
||||
var onKeyboardDismissed: (() -> Void)? // Called when keyboard is dismissed
|
||||
|
||||
func makeUIView(context: Context) -> SwiftUIEmojiTextField {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,10 @@ struct MessageContextMenuItems: View {
|
|||
}
|
||||
|
||||
Button("Tapback") {
|
||||
isShowingTapbackInput = true
|
||||
// The context menu needs a moment to dismiss before the focus state can be changed.
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
isShowingTapbackInput = true
|
||||
}
|
||||
}
|
||||
|
||||
Button(action: onReply) {
|
||||
|
|
|
|||
|
|
@ -30,8 +30,10 @@ struct MessageText: View {
|
|||
@State private var isShowingTapbackInput = false
|
||||
@State private var tapbackText = ""
|
||||
|
||||
@FocusState private var isTapbackInputFocused: Bool
|
||||
@State private var tapbackText = ""
|
||||
|
||||
var body: some View {
|
||||
|
||||
SessionReplayPrivacyView(textAndInputPrivacy: .maskAll) {
|
||||
let markdownText = LocalizedStringKey(message.messagePayloadMarkdown ?? (message.messagePayload ?? "EMPTY MESSAGE"))
|
||||
Text(markdownText)
|
||||
|
|
@ -96,35 +98,10 @@ struct MessageText: View {
|
|||
onReply: onReply
|
||||
)
|
||||
}
|
||||
messageContent
|
||||
.environment(\.openURL, OpenURLAction { url in
|
||||
saveChannelLink = nil
|
||||
var addChannels = false
|
||||
if url.absoluteString.lowercased().contains("meshtastic.org/v/#") {
|
||||
// Handle contact URL
|
||||
ContactURLHandler.handleContactUrl(url: url, accessoryManager: AccessoryManager.shared)
|
||||
return .handled // Prevent default browser opening
|
||||
} else if url.absoluteString.lowercased().contains("meshtastic.org/e/") {
|
||||
// Handle channel URL
|
||||
let components = url.absoluteString.components(separatedBy: "#")
|
||||
guard !components.isEmpty, let lastComponent = components.last else {
|
||||
Logger.services.error("No valid components found in channel URL: \(url.absoluteString, privacy: .public)")
|
||||
return .discarded
|
||||
}
|
||||
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.saveChannelLink = nil
|
||||
return .discarded
|
||||
}
|
||||
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
|
||||
handleURL(url)
|
||||
})
|
||||
// Display sheet for channel settings
|
||||
.sheet(item: $saveChannelLink) { link in
|
||||
SaveChannelQRCode(
|
||||
channelSetLink: link.data,
|
||||
|
|
@ -170,17 +147,155 @@ struct MessageText: View {
|
|||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Delete Message", role: .destructive) {
|
||||
context.delete(message)
|
||||
do {
|
||||
try context.save()
|
||||
} catch {
|
||||
Logger.data.error("Failed to delete message \(message.messageId, privacy: .public): \(error.localizedDescription, privacy: .public)")
|
||||
}
|
||||
deleteMessage()
|
||||
}
|
||||
Button("Cancel", role: .cancel) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var messageContent: some View {
|
||||
let markdownText = LocalizedStringKey(message.messagePayloadMarkdown ?? (message.messagePayload ?? "EMPTY MESSAGE"))
|
||||
return Text(markdownText)
|
||||
.tint(Self.linkBlue)
|
||||
.padding(.vertical, 10)
|
||||
.padding(.horizontal, 8)
|
||||
.foregroundColor(.white)
|
||||
.background(isCurrentUser ? .accentColor : Color(.gray))
|
||||
.cornerRadius(15)
|
||||
.background {
|
||||
TextField("", text: $tapbackText)
|
||||
.keyboardType(.emoji)
|
||||
.scrollDismissesKeyboard(.immediately)
|
||||
.focused($isTapbackInputFocused)
|
||||
.frame(width: 0, height: 0)
|
||||
.opacity(0)
|
||||
.onChange(of: tapbackText) {
|
||||
processTapback()
|
||||
}
|
||||
}
|
||||
.overlay(messageOverlays)
|
||||
.contextMenu {
|
||||
MessageContextMenuItems(
|
||||
message: message,
|
||||
tapBackDestination: tapBackDestination,
|
||||
isCurrentUser: isCurrentUser,
|
||||
isShowingDeleteConfirmation: $isShowingDeleteConfirmation,
|
||||
isShowingTapbackInput: Binding(
|
||||
get: { isTapbackInputFocused },
|
||||
set: { isTapbackInputFocused = $0 }
|
||||
),
|
||||
onReply: onReply
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var messageOverlays: some View {
|
||||
if message.pkiEncrypted && message.realACK || !isCurrentUser && message.pkiEncrypted {
|
||||
VStack(alignment: .trailing) {
|
||||
Spacer()
|
||||
HStack {
|
||||
Spacer()
|
||||
Image(systemName: "lock.circle.fill")
|
||||
.symbolRenderingMode(.palette)
|
||||
.foregroundStyle(.white, .green)
|
||||
.font(.system(size: 20))
|
||||
.offset(x: 8, y: 8)
|
||||
}
|
||||
}
|
||||
}
|
||||
if message.portNum == Int32(PortNum.storeForwardApp.rawValue) {
|
||||
VStack(alignment: .trailing) {
|
||||
Spacer()
|
||||
HStack {
|
||||
Spacer()
|
||||
Image(systemName: "envelope.circle.fill")
|
||||
.symbolRenderingMode(.palette)
|
||||
.foregroundStyle(.white, .gray)
|
||||
.font(.system(size: 20))
|
||||
.offset(x: 8, y: 8)
|
||||
}
|
||||
}
|
||||
}
|
||||
if tapBackDestination.overlaySensorMessage && message.portNum == Int32(PortNum.detectionSensorApp.rawValue) {
|
||||
Image(systemName: "sensor.fill")
|
||||
.padding()
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing)
|
||||
.foregroundStyle(Color.orange)
|
||||
.symbolRenderingMode(.multicolor)
|
||||
.symbolEffect(.variableColor.reversing.cumulative, options: .repeat(20).speed(3))
|
||||
.offset(x: 20, y: -20)
|
||||
}
|
||||
}
|
||||
|
||||
private func handleURL(_ url: URL) -> OpenURLAction.Result {
|
||||
saveChannelLink = nil
|
||||
var addChannels = false
|
||||
if url.absoluteString.lowercased().contains("meshtastic.org/v/#") {
|
||||
// Handle contact URL
|
||||
ContactURLHandler.handleContactUrl(url: url, accessoryManager: AccessoryManager.shared)
|
||||
return .handled // Prevent default browser opening
|
||||
} else if url.absoluteString.lowercased().contains("meshtastic.org/e/") {
|
||||
// Handle channel URL
|
||||
let components = url.absoluteString.components(separatedBy: "#")
|
||||
guard !components.isEmpty, let lastComponent = components.last else {
|
||||
Logger.services.error("No valid components found in channel URL: \(url.absoluteString, privacy: .public)")
|
||||
return .discarded
|
||||
}
|
||||
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.saveChannelLink = nil
|
||||
return .discarded
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
private func deleteMessage() {
|
||||
context.delete(message)
|
||||
do {
|
||||
try context.save()
|
||||
} catch {
|
||||
Logger.data.error("Failed to delete message \(message.messageId, privacy: .public): \(error.localizedDescription, privacy: .public)")
|
||||
}
|
||||
}
|
||||
|
||||
private func processTapback() {
|
||||
guard !tapbackText.isEmpty else { return }
|
||||
let emojiToSend = tapbackText
|
||||
|
||||
Task {
|
||||
do {
|
||||
try await accessoryManager.sendMessage(
|
||||
message: emojiToSend,
|
||||
toUserNum: tapBackDestination.userNum,
|
||||
channel: tapBackDestination.channelNum,
|
||||
isEmoji: true,
|
||||
replyID: message.messageId
|
||||
)
|
||||
await MainActor.run {
|
||||
switch tapBackDestination {
|
||||
case let .channel(channel):
|
||||
context.refresh(channel, mergeChanges: true)
|
||||
case let .user(user):
|
||||
context.refresh(user, mergeChanges: true)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
Logger.services.warning("Failed to send tapback.")
|
||||
}
|
||||
}
|
||||
|
||||
tapbackText = ""
|
||||
isTapbackInputFocused = false
|
||||
}
|
||||
}
|
||||
|
||||
private extension MessageDestination {
|
||||
|
|
|
|||
|
|
@ -9,40 +9,25 @@ struct TapbackInputView: View {
|
|||
var body: some View {
|
||||
NavigationView {
|
||||
VStack(spacing: 0) {
|
||||
EmojiOnlyTextField(
|
||||
text: $text,
|
||||
placeholder: "Tap to enter emoji",
|
||||
onBecomeFirstResponder: {
|
||||
// Text field will automatically become first responder
|
||||
},
|
||||
onKeyboardTypeChanged: { shouldDismiss in
|
||||
// Dismiss if keyboard switched away from emoji
|
||||
if shouldDismiss {
|
||||
isPresented = false
|
||||
TextField("Tap to enter emoji", text: $text)
|
||||
.keyboardType(.emoji)
|
||||
.frame(height: 50)
|
||||
.padding(.horizontal)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.strokeBorder(.tertiary, lineWidth: 1)
|
||||
.background(RoundedRectangle(cornerRadius: 10).fill(Color(.systemBackground)))
|
||||
)
|
||||
.padding(.horizontal)
|
||||
.padding(.top, 8)
|
||||
.onChange(of: text) { oldValue, newValue in
|
||||
// Extract first emoji character and send it
|
||||
if !newValue.isEmpty, let firstEmoji = extractFirstEmoji(from: newValue) {
|
||||
onEmojiSelected(firstEmoji)
|
||||
// Clear the text box after getting the emoji
|
||||
text = ""
|
||||
}
|
||||
},
|
||||
onKeyboardDismissed: {
|
||||
// Dismiss sheet when keyboard is dismissed
|
||||
isPresented = false
|
||||
}
|
||||
)
|
||||
.frame(height: 50)
|
||||
.padding(.horizontal)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.strokeBorder(.tertiary, lineWidth: 1)
|
||||
.background(RoundedRectangle(cornerRadius: 10).fill(Color(.systemBackground)))
|
||||
)
|
||||
.padding(.horizontal)
|
||||
.padding(.top, 8)
|
||||
.onChange(of: text) { oldValue, newValue in
|
||||
// Extract first emoji character and send it
|
||||
if !newValue.isEmpty, let firstEmoji = extractFirstEmoji(from: newValue) {
|
||||
onEmojiSelected(firstEmoji)
|
||||
// Clear the text box after getting the emoji
|
||||
text = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Tapback")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
import CoreData
|
||||
import SwiftUI
|
||||
import OSLog
|
||||
|
||||
struct ExchangeUserInfoButton: View {
|
||||
var node: NodeInfoEntity
|
||||
var connectedNode: NodeInfoEntity
|
||||
|
||||
@EnvironmentObject var accessoryManager: AccessoryManager
|
||||
|
||||
@State private var isPresentingUserInfoSentAlert: Bool = false
|
||||
@State private var isPresentingUserInfoFailedAlert: Bool = false
|
||||
|
||||
var body: some View {
|
||||
Button {
|
||||
Task {
|
||||
if let fromUser = connectedNode.user, let toUser = node.user {
|
||||
do {
|
||||
_ = try await accessoryManager.exchangeUserInfo(fromUser: fromUser, toUser: toUser)
|
||||
Task { @MainActor in
|
||||
isPresentingUserInfoSentAlert = true
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
|
||||
isPresentingUserInfoSentAlert = false
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
Logger.mesh.warning("Failed to exchange user info")
|
||||
Task { @MainActor in
|
||||
isPresentingUserInfoFailedAlert = true
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
|
||||
isPresentingUserInfoFailedAlert = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} label: {
|
||||
Label {
|
||||
Text("Exchange User Info")
|
||||
} icon: {
|
||||
Image(systemName: "person.2.badge.gearshape")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
}
|
||||
}.alert(
|
||||
"User Info Sent",
|
||||
isPresented: $isPresentingUserInfoSentAlert
|
||||
) {
|
||||
Button("OK") { }.keyboardShortcut(.defaultAction)
|
||||
} message: {
|
||||
Text("Your user info has been sent with a request for a response with their user info.")
|
||||
}.alert(
|
||||
"User Info Exchange Failed",
|
||||
isPresented: $isPresentingUserInfoFailedAlert
|
||||
) {
|
||||
Button("OK") { }.keyboardShortcut(.defaultAction)
|
||||
} message: {
|
||||
Text("Failed to exchange user info.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -29,7 +29,6 @@ struct WaypointForm: View {
|
|||
@State private var expire: Date = Date.now.addingTimeInterval(60 * 480) // 1 minute * 480 = 8 Hours
|
||||
@State private var locked: Bool = false
|
||||
@State private var lockedTo: Int64 = 0
|
||||
@State private var detents: Set<PresentationDetent> = [.medium, .fraction(0.85)]
|
||||
@State private var selectedDetent: PresentationDetent = .medium
|
||||
@State private var waypointFailedAlert: Bool = false
|
||||
|
||||
|
|
@ -111,26 +110,19 @@ struct WaypointForm: View {
|
|||
HStack {
|
||||
Text("Icon")
|
||||
Spacer()
|
||||
EmojiOnlyTextField(text: $icon, placeholder: "Select an emoji")
|
||||
TextField("Select an emoji", text: $icon)
|
||||
.keyboardType(.emoji)
|
||||
.font(.title)
|
||||
.focused($iconIsFocused)
|
||||
.onChange(of: icon) { _, value in
|
||||
|
||||
// If you have anything other than emojis in your string make it empty
|
||||
if !value.onlyEmojis() {
|
||||
icon = ""
|
||||
}
|
||||
// If a second emoji is entered delete the first one
|
||||
if value.count >= 1 {
|
||||
|
||||
if value.count > 1 {
|
||||
let index = value.index(value.startIndex, offsetBy: 1)
|
||||
icon = String(value[index])
|
||||
}
|
||||
iconIsFocused = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Toggle(isOn: $expires) {
|
||||
Label("Expires", systemImage: "clock.badge.xmark")
|
||||
|
|
@ -458,7 +450,6 @@ struct WaypointForm: View {
|
|||
longitude = waypoint.coordinate.longitude
|
||||
}
|
||||
}
|
||||
.presentationDetents(detents, selection: $selectedDetent)
|
||||
.presentationBackgroundInteraction(.enabled(upThrough: .fraction(0.85)))
|
||||
.presentationDragIndicator(.visible)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -464,6 +464,10 @@ struct NodeDetail: View {
|
|||
node: node,
|
||||
connectedNode: connectedNode
|
||||
)
|
||||
ExchangeUserInfoButton(
|
||||
node: node,
|
||||
connectedNode: connectedNode
|
||||
)
|
||||
TraceRouteButton(
|
||||
node: node
|
||||
)
|
||||
|
|
|
|||
|
|
@ -120,16 +120,14 @@ struct MeshMap: View {
|
|||
}
|
||||
.sheet(item: $selectedWaypoint) { selection in
|
||||
WaypointForm(waypoint: selection)
|
||||
.padding()
|
||||
.presentationDetents([.large])
|
||||
}
|
||||
.sheet(item: $editingWaypoint) { selection in
|
||||
WaypointForm(waypoint: selection, editMode: true)
|
||||
.padding()
|
||||
.presentationDetents([.large])
|
||||
}
|
||||
.sheet(isPresented: $editingSettings) {
|
||||
MapSettingsForm(traffic: $showTraffic, pointsOfInterest: $showPointsOfInterest, mapLayer: $selectedMapLayer, meshMap: $isMeshMap, enabledOverlayConfigs: $enabledOverlayConfigs)
|
||||
.presentationDetents([.large])
|
||||
|
||||
}
|
||||
.onChange(of: router.navigationState) {
|
||||
guard case .map = router.navigationState.selectedTab else { return }
|
||||
|
|
|
|||
|
|
@ -253,6 +253,19 @@ fileprivate struct FilteredNodeList: View {
|
|||
} label: {
|
||||
Label("Exchange Positions", systemImage: "arrow.triangle.2.circlepath")
|
||||
}
|
||||
Button {
|
||||
Task {
|
||||
if let fromUser = connectedNode.user, let toUser = node.user {
|
||||
do {
|
||||
_ = try await accessoryManager.exchangeUserInfo(fromUser: fromUser, toUser: toUser)
|
||||
} catch {
|
||||
Logger.mesh.warning("Failed to exchange user info")
|
||||
}
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Label("Exchange User Info", systemImage: "person.2.badge.gearshape")
|
||||
}
|
||||
TraceRouteButton(
|
||||
node: node
|
||||
)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,55 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
typealias Version = _2
|
||||
}
|
||||
|
||||
///
|
||||
/// Firmware update mode for OTA updates
|
||||
public enum OTAMode: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
/// Do not reboot into OTA mode
|
||||
case noRebootOta // = 0
|
||||
|
||||
///
|
||||
/// Reboot into OTA mode for BLE firmware update
|
||||
case otaBle // = 1
|
||||
|
||||
///
|
||||
/// Reboot into OTA mode for WiFi firmware update
|
||||
case otaWifi // = 2
|
||||
case UNRECOGNIZED(Int)
|
||||
|
||||
public init() {
|
||||
self = .noRebootOta
|
||||
}
|
||||
|
||||
public init?(rawValue: Int) {
|
||||
switch rawValue {
|
||||
case 0: self = .noRebootOta
|
||||
case 1: self = .otaBle
|
||||
case 2: self = .otaWifi
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
public var rawValue: Int {
|
||||
switch self {
|
||||
case .noRebootOta: return 0
|
||||
case .otaBle: return 1
|
||||
case .otaWifi: return 2
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [OTAMode] = [
|
||||
.noRebootOta,
|
||||
.otaBle,
|
||||
.otaWifi,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
///
|
||||
/// 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.
|
||||
|
|
@ -478,6 +527,16 @@ public struct AdminMessage: Sendable {
|
|||
set {payloadVariant = .removeIgnoredNode(newValue)}
|
||||
}
|
||||
|
||||
///
|
||||
/// Set specified node-num to be muted
|
||||
public var toggleMutedNode: UInt32 {
|
||||
get {
|
||||
if case .toggleMutedNode(let v)? = payloadVariant {return v}
|
||||
return 0
|
||||
}
|
||||
set {payloadVariant = .toggleMutedNode(newValue)}
|
||||
}
|
||||
|
||||
///
|
||||
/// Begins an edit transaction for config, module config, owner, and channel settings changes
|
||||
/// This will delay the standard *implicit* save to the file system and subsequent reboot behavior until committed (commit_edit_settings)
|
||||
|
|
@ -532,6 +591,9 @@ public struct AdminMessage: Sendable {
|
|||
///
|
||||
/// Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot)
|
||||
/// Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth.
|
||||
/// Deprecated in favor of reboot_ota_mode in 2.7.17
|
||||
///
|
||||
/// NOTE: This field was marked as deprecated in the .proto file.
|
||||
public var rebootOtaSeconds: Int32 {
|
||||
get {
|
||||
if case .rebootOtaSeconds(let v)? = payloadVariant {return v}
|
||||
|
|
@ -592,6 +654,16 @@ public struct AdminMessage: Sendable {
|
|||
set {payloadVariant = .nodedbReset(newValue)}
|
||||
}
|
||||
|
||||
///
|
||||
/// Tell the node to reset into the OTA Loader
|
||||
public var otaRequest: AdminMessage.OTAEvent {
|
||||
get {
|
||||
if case .otaRequest(let v)? = payloadVariant {return v}
|
||||
return AdminMessage.OTAEvent()
|
||||
}
|
||||
set {payloadVariant = .otaRequest(newValue)}
|
||||
}
|
||||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
///
|
||||
|
|
@ -735,6 +807,9 @@ public struct AdminMessage: Sendable {
|
|||
/// Set specified node-num to be un-ignored on the NodeDB on the device
|
||||
case removeIgnoredNode(UInt32)
|
||||
///
|
||||
/// Set specified node-num to be muted
|
||||
case toggleMutedNode(UInt32)
|
||||
///
|
||||
/// Begins an edit transaction for config, module config, owner, and channel settings changes
|
||||
/// This will delay the standard *implicit* save to the file system and subsequent reboot behavior until committed (commit_edit_settings)
|
||||
case beginEditSettings(Bool)
|
||||
|
|
@ -753,6 +828,9 @@ public struct AdminMessage: Sendable {
|
|||
///
|
||||
/// Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot)
|
||||
/// Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth.
|
||||
/// Deprecated in favor of reboot_ota_mode in 2.7.17
|
||||
///
|
||||
/// NOTE: This field was marked as deprecated in the .proto file.
|
||||
case rebootOtaSeconds(Int32)
|
||||
///
|
||||
/// This message is only supported for the simulator Portduino build.
|
||||
|
|
@ -771,6 +849,9 @@ public struct AdminMessage: Sendable {
|
|||
/// Tell the node to reset the nodedb.
|
||||
/// When true, favorites are preserved through reset.
|
||||
case nodedbReset(Bool)
|
||||
///
|
||||
/// Tell the node to reset into the OTA Loader
|
||||
case otaRequest(AdminMessage.OTAEvent)
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -1059,6 +1140,28 @@ public struct AdminMessage: Sendable {
|
|||
public init() {}
|
||||
}
|
||||
|
||||
///
|
||||
/// User is requesting an over the air update.
|
||||
/// Node will reboot into the OTA loader
|
||||
public struct OTAEvent: 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.
|
||||
|
||||
///
|
||||
/// Tell the node to reboot into OTA mode for firmware update via BLE or WiFi (ESP32 only for now)
|
||||
public var rebootOtaMode: OTAMode = .noRebootOta
|
||||
|
||||
///
|
||||
/// A 32 byte hash of the OTA firmware.
|
||||
/// Used to verify the integrity of the firmware before applying an update.
|
||||
public var otaHash: Data = Data()
|
||||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
|
|
@ -1239,9 +1342,13 @@ public struct KeyVerificationAdmin: Sendable {
|
|||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
|
||||
extension OTAMode: SwiftProtobuf._ProtoNameProviding {
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0NO_REBOOT_OTA\0\u{1}OTA_BLE\0\u{1}OTA_WIFI\0")
|
||||
}
|
||||
|
||||
extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = _protobuf_package + ".AdminMessage"
|
||||
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 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{3}toggle_muted_node\0\u{4}\u{f}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\u{3}ota_request\0")
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
|
|
@ -1673,6 +1780,14 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
|
|||
self.payloadVariant = .removeIgnoredNode(v)
|
||||
}
|
||||
}()
|
||||
case 49: try {
|
||||
var v: UInt32?
|
||||
try decoder.decodeSingularUInt32Field(value: &v)
|
||||
if let v = v {
|
||||
if self.payloadVariant != nil {try decoder.handleConflictingOneOf()}
|
||||
self.payloadVariant = .toggleMutedNode(v)
|
||||
}
|
||||
}()
|
||||
case 64: try {
|
||||
var v: Bool?
|
||||
try decoder.decodeSingularBoolField(value: &v)
|
||||
|
|
@ -1772,6 +1887,19 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
|
|||
}
|
||||
}()
|
||||
case 101: try { try decoder.decodeSingularBytesField(value: &self.sessionPasskey) }()
|
||||
case 102: try {
|
||||
var v: AdminMessage.OTAEvent?
|
||||
var hadOneofValue = false
|
||||
if let current = self.payloadVariant {
|
||||
hadOneofValue = true
|
||||
if case .otaRequest(let m) = current {v = m}
|
||||
}
|
||||
try decoder.decodeSingularMessageField(value: &v)
|
||||
if let v = v {
|
||||
if hadOneofValue {try decoder.handleConflictingOneOf()}
|
||||
self.payloadVariant = .otaRequest(v)
|
||||
}
|
||||
}()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -1955,6 +2083,10 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
|
|||
guard case .removeIgnoredNode(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularUInt32Field(value: v, fieldNumber: 48)
|
||||
}()
|
||||
case .toggleMutedNode?: try {
|
||||
guard case .toggleMutedNode(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularUInt32Field(value: v, fieldNumber: 49)
|
||||
}()
|
||||
case .beginEditSettings?: try {
|
||||
guard case .beginEditSettings(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularBoolField(value: v, fieldNumber: 64)
|
||||
|
|
@ -1999,11 +2131,14 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
|
|||
guard case .nodedbReset(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularBoolField(value: v, fieldNumber: 100)
|
||||
}()
|
||||
case nil: break
|
||||
default: break
|
||||
}
|
||||
if !self.sessionPasskey.isEmpty {
|
||||
try visitor.visitSingularBytesField(value: self.sessionPasskey, fieldNumber: 101)
|
||||
}
|
||||
try { if case .otaRequest(let v)? = self.payloadVariant {
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 102)
|
||||
} }()
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
|
|
@ -2072,6 +2207,41 @@ extension AdminMessage.InputEvent: SwiftProtobuf.Message, SwiftProtobuf._Message
|
|||
}
|
||||
}
|
||||
|
||||
extension AdminMessage.OTAEvent: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = AdminMessage.protoMessageName + ".OTAEvent"
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}reboot_ota_mode\0\u{3}ota_hash\0")
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
// The use of inline closures is to circumvent an issue where the compiler
|
||||
// allocates stack space for every case branch when no optimizations are
|
||||
// enabled. https://github.com/apple/swift-protobuf/issues/1034
|
||||
switch fieldNumber {
|
||||
case 1: try { try decoder.decodeSingularEnumField(value: &self.rebootOtaMode) }()
|
||||
case 2: try { try decoder.decodeSingularBytesField(value: &self.otaHash) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
if self.rebootOtaMode != .noRebootOta {
|
||||
try visitor.visitSingularEnumField(value: self.rebootOtaMode, fieldNumber: 1)
|
||||
}
|
||||
if !self.otaHash.isEmpty {
|
||||
try visitor.visitSingularBytesField(value: self.otaHash, fieldNumber: 2)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
public static func ==(lhs: AdminMessage.OTAEvent, rhs: AdminMessage.OTAEvent) -> Bool {
|
||||
if lhs.rebootOtaMode != rhs.rebootOtaMode {return false}
|
||||
if lhs.otaHash != rhs.otaHash {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension HamParameters: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = _protobuf_package + ".HamParameters"
|
||||
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")
|
||||
|
|
|
|||
|
|
@ -281,7 +281,7 @@ public struct Config: Sendable {
|
|||
case routerLate // = 11
|
||||
|
||||
///
|
||||
/// Description: Treats packets from or to favorited nodes as ROUTER, and all other packets as CLIENT.
|
||||
/// Description: Treats packets from or to favorited nodes as ROUTER_LATE, and all other packets as CLIENT.
|
||||
/// Technical Details: Used for stronger attic/roof nodes to distribute messages more widely
|
||||
/// from weaker, indoor, or less-well-positioned nodes. Recommended for users with multiple nodes
|
||||
/// where one CLIENT_BASE acts as a more powerful base station, such as an attic/roof node.
|
||||
|
|
|
|||
|
|
@ -230,6 +230,7 @@ public struct NodeInfoLite: @unchecked Sendable {
|
|||
///
|
||||
/// Bitfield for storing booleans.
|
||||
/// LSB 0 is_key_manually_verified
|
||||
/// LSB 1 is_muted
|
||||
public var bitfield: UInt32 {
|
||||
get {return _storage._bitfield}
|
||||
set {_uniqueStorage()._bitfield = newValue}
|
||||
|
|
|
|||
|
|
@ -166,8 +166,8 @@ public enum HardwareModel: SwiftProtobuf.Enum, Swift.CaseIterable {
|
|||
case loraRelayV1 // = 32
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case nrf52840Dk // = 33
|
||||
/// T-Echo Plus device from LilyGo
|
||||
case tEchoPlus // = 33
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
|
|
@ -535,6 +535,18 @@ public enum HardwareModel: SwiftProtobuf.Enum, Swift.CaseIterable {
|
|||
/// Elecrow ThinkNode M6
|
||||
case thinknodeM6 // = 120
|
||||
|
||||
///
|
||||
/// Elecrow Meshstick 1262
|
||||
case meshstick1262 // = 121
|
||||
|
||||
///
|
||||
/// LilyGo T-Beam 1W
|
||||
case tbeam1Watt // = 122
|
||||
|
||||
///
|
||||
/// LilyGo T5 S3 ePaper Pro (V1 and V2)
|
||||
case t5S3EpaperPro // = 123
|
||||
|
||||
///
|
||||
/// ------------------------------------------------------------------------------------------------------------------------------------------
|
||||
/// Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
|
||||
|
|
@ -581,7 +593,7 @@ public enum HardwareModel: SwiftProtobuf.Enum, Swift.CaseIterable {
|
|||
case 30: self = .rp2040Lora
|
||||
case 31: self = .stationG2
|
||||
case 32: self = .loraRelayV1
|
||||
case 33: self = .nrf52840Dk
|
||||
case 33: self = .tEchoPlus
|
||||
case 34: self = .ppr
|
||||
case 35: self = .genieblocks
|
||||
case 36: self = .nrf52Unknown
|
||||
|
|
@ -669,6 +681,9 @@ public enum HardwareModel: SwiftProtobuf.Enum, Swift.CaseIterable {
|
|||
case 118: self = .rak6421
|
||||
case 119: self = .thinknodeM4
|
||||
case 120: self = .thinknodeM6
|
||||
case 121: self = .meshstick1262
|
||||
case 122: self = .tbeam1Watt
|
||||
case 123: self = .t5S3EpaperPro
|
||||
case 255: self = .privateHw
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
}
|
||||
|
|
@ -709,7 +724,7 @@ public enum HardwareModel: SwiftProtobuf.Enum, Swift.CaseIterable {
|
|||
case .rp2040Lora: return 30
|
||||
case .stationG2: return 31
|
||||
case .loraRelayV1: return 32
|
||||
case .nrf52840Dk: return 33
|
||||
case .tEchoPlus: return 33
|
||||
case .ppr: return 34
|
||||
case .genieblocks: return 35
|
||||
case .nrf52Unknown: return 36
|
||||
|
|
@ -797,6 +812,9 @@ public enum HardwareModel: SwiftProtobuf.Enum, Swift.CaseIterable {
|
|||
case .rak6421: return 118
|
||||
case .thinknodeM4: return 119
|
||||
case .thinknodeM6: return 120
|
||||
case .meshstick1262: return 121
|
||||
case .tbeam1Watt: return 122
|
||||
case .t5S3EpaperPro: return 123
|
||||
case .privateHw: return 255
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
}
|
||||
|
|
@ -837,7 +855,7 @@ public enum HardwareModel: SwiftProtobuf.Enum, Swift.CaseIterable {
|
|||
.rp2040Lora,
|
||||
.stationG2,
|
||||
.loraRelayV1,
|
||||
.nrf52840Dk,
|
||||
.tEchoPlus,
|
||||
.ppr,
|
||||
.genieblocks,
|
||||
.nrf52Unknown,
|
||||
|
|
@ -925,6 +943,9 @@ public enum HardwareModel: SwiftProtobuf.Enum, Swift.CaseIterable {
|
|||
.rak6421,
|
||||
.thinknodeM4,
|
||||
.thinknodeM6,
|
||||
.meshstick1262,
|
||||
.tbeam1Watt,
|
||||
.t5S3EpaperPro,
|
||||
.privateHw,
|
||||
]
|
||||
|
||||
|
|
@ -1921,6 +1942,11 @@ public struct Routing: Sendable {
|
|||
/// Airtime fairness rate limit exceeded for a packet
|
||||
/// This typically enforced per portnum and is used to prevent a single node from monopolizing airtime
|
||||
case rateLimitExceeded // = 38
|
||||
|
||||
///
|
||||
/// PKI encryption failed, due to no public key for the remote node
|
||||
/// This is different from PKI_UNKNOWN_PUBKEY which indicates a failure upon receiving a packet
|
||||
case pkiSendFailPublicKey // = 39
|
||||
case UNRECOGNIZED(Int)
|
||||
|
||||
public init() {
|
||||
|
|
@ -1946,6 +1972,7 @@ public struct Routing: Sendable {
|
|||
case 36: self = .adminBadSessionKey
|
||||
case 37: self = .adminPublicKeyUnauthorized
|
||||
case 38: self = .rateLimitExceeded
|
||||
case 39: self = .pkiSendFailPublicKey
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
}
|
||||
}
|
||||
|
|
@ -1969,6 +1996,7 @@ public struct Routing: Sendable {
|
|||
case .adminBadSessionKey: return 36
|
||||
case .adminPublicKeyUnauthorized: return 37
|
||||
case .rateLimitExceeded: return 38
|
||||
case .pkiSendFailPublicKey: return 39
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
}
|
||||
}
|
||||
|
|
@ -1992,6 +2020,7 @@ public struct Routing: Sendable {
|
|||
.adminBadSessionKey,
|
||||
.adminPublicKeyUnauthorized,
|
||||
.rateLimitExceeded,
|
||||
.pkiSendFailPublicKey,
|
||||
]
|
||||
|
||||
}
|
||||
|
|
@ -2136,6 +2165,10 @@ public struct StoreForwardPlusPlus: Sendable {
|
|||
/// The receive time of the message in question
|
||||
public var encapsulatedRxtime: UInt32 = 0
|
||||
|
||||
///
|
||||
/// Used in a LINK_REQUEST to specify the message X spots back from head
|
||||
public var chainCount: UInt32 = 0
|
||||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
///
|
||||
|
|
@ -2936,6 +2969,14 @@ public struct NodeInfo: @unchecked Sendable {
|
|||
set {_uniqueStorage()._isKeyManuallyVerified = newValue}
|
||||
}
|
||||
|
||||
///
|
||||
/// True if node has been muted
|
||||
/// Persistes between NodeDB internal clean ups
|
||||
public var isMuted: Bool {
|
||||
get {return _storage._isMuted}
|
||||
set {_uniqueStorage()._isMuted = newValue}
|
||||
}
|
||||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
public init() {}
|
||||
|
|
@ -3959,7 +4000,7 @@ public struct ChunkedPayloadResponse: Sendable {
|
|||
fileprivate let _protobuf_package = "meshtastic"
|
||||
|
||||
extension HardwareModel: SwiftProtobuf._ProtoNameProviding {
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0UNSET\0\u{1}TLORA_V2\0\u{1}TLORA_V1\0\u{1}TLORA_V2_1_1P6\0\u{1}TBEAM\0\u{1}HELTEC_V2_0\0\u{1}TBEAM_V0P7\0\u{1}T_ECHO\0\u{1}TLORA_V1_1P3\0\u{1}RAK4631\0\u{1}HELTEC_V2_1\0\u{1}HELTEC_V1\0\u{1}LILYGO_TBEAM_S3_CORE\0\u{1}RAK11200\0\u{1}NANO_G1\0\u{1}TLORA_V2_1_1P8\0\u{1}TLORA_T3_S3\0\u{1}NANO_G1_EXPLORER\0\u{1}NANO_G2_ULTRA\0\u{1}LORA_TYPE\0\u{1}WIPHONE\0\u{1}WIO_WM1110\0\u{1}RAK2560\0\u{1}HELTEC_HRU_3601\0\u{1}HELTEC_WIRELESS_BRIDGE\0\u{1}STATION_G1\0\u{1}RAK11310\0\u{1}SENSELORA_RP2040\0\u{1}SENSELORA_S3\0\u{1}CANARYONE\0\u{1}RP2040_LORA\0\u{1}STATION_G2\0\u{1}LORA_RELAY_V1\0\u{1}NRF52840DK\0\u{1}PPR\0\u{1}GENIEBLOCKS\0\u{1}NRF52_UNKNOWN\0\u{1}PORTDUINO\0\u{1}ANDROID_SIM\0\u{1}DIY_V1\0\u{1}NRF52840_PCA10059\0\u{1}DR_DEV\0\u{1}M5STACK\0\u{1}HELTEC_V3\0\u{1}HELTEC_WSL_V3\0\u{1}BETAFPV_2400_TX\0\u{1}BETAFPV_900_NANO_TX\0\u{1}RPI_PICO\0\u{1}HELTEC_WIRELESS_TRACKER\0\u{1}HELTEC_WIRELESS_PAPER\0\u{1}T_DECK\0\u{1}T_WATCH_S3\0\u{1}PICOMPUTER_S3\0\u{1}HELTEC_HT62\0\u{1}EBYTE_ESP32_S3\0\u{1}ESP32_S3_PICO\0\u{1}CHATTER_2\0\u{1}HELTEC_WIRELESS_PAPER_V1_0\0\u{1}HELTEC_WIRELESS_TRACKER_V1_0\0\u{1}UNPHONE\0\u{1}TD_LORAC\0\u{1}CDEBYTE_EORA_S3\0\u{1}TWC_MESH_V4\0\u{1}NRF52_PROMICRO_DIY\0\u{1}RADIOMASTER_900_BANDIT_NANO\0\u{1}HELTEC_CAPSULE_SENSOR_V3\0\u{1}HELTEC_VISION_MASTER_T190\0\u{1}HELTEC_VISION_MASTER_E213\0\u{1}HELTEC_VISION_MASTER_E290\0\u{1}HELTEC_MESH_NODE_T114\0\u{1}SENSECAP_INDICATOR\0\u{1}TRACKER_T1000_E\0\u{1}RAK3172\0\u{1}WIO_E5\0\u{1}RADIOMASTER_900_BANDIT\0\u{1}ME25LS01_4Y10TD\0\u{1}RP2040_FEATHER_RFM95\0\u{1}M5STACK_COREBASIC\0\u{1}M5STACK_CORE2\0\u{1}RPI_PICO2\0\u{1}M5STACK_CORES3\0\u{1}SEEED_XIAO_S3\0\u{1}MS24SF1\0\u{1}TLORA_C6\0\u{1}WISMESH_TAP\0\u{1}ROUTASTIC\0\u{1}MESH_TAB\0\u{1}MESHLINK\0\u{1}XIAO_NRF52_KIT\0\u{1}THINKNODE_M1\0\u{1}THINKNODE_M2\0\u{1}T_ETH_ELITE\0\u{1}HELTEC_SENSOR_HUB\0\u{1}MUZI_BASE\0\u{1}HELTEC_MESH_POCKET\0\u{1}SEEED_SOLAR_NODE\0\u{1}NOMADSTAR_METEOR_PRO\0\u{1}CROWPANEL\0\u{1}LINK_32\0\u{1}SEEED_WIO_TRACKER_L1\0\u{1}SEEED_WIO_TRACKER_L1_EINK\0\u{1}MUZI_R1_NEO\0\u{1}T_DECK_PRO\0\u{1}T_LORA_PAGER\0\u{1}M5STACK_RESERVED\0\u{1}WISMESH_TAG\0\u{1}RAK3312\0\u{1}THINKNODE_M5\0\u{1}HELTEC_MESH_SOLAR\0\u{1}T_ECHO_LITE\0\u{1}HELTEC_V4\0\u{1}M5STACK_C6L\0\u{1}M5STACK_CARDPUTER_ADV\0\u{1}HELTEC_WIRELESS_TRACKER_V2\0\u{1}T_WATCH_ULTRA\0\u{1}THINKNODE_M3\0\u{1}WISMESH_TAP_V2\0\u{1}RAK3401\0\u{1}RAK6421\0\u{1}THINKNODE_M4\0\u{1}THINKNODE_M6\0\u{2}G\u{2}PRIVATE_HW\0")
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0UNSET\0\u{1}TLORA_V2\0\u{1}TLORA_V1\0\u{1}TLORA_V2_1_1P6\0\u{1}TBEAM\0\u{1}HELTEC_V2_0\0\u{1}TBEAM_V0P7\0\u{1}T_ECHO\0\u{1}TLORA_V1_1P3\0\u{1}RAK4631\0\u{1}HELTEC_V2_1\0\u{1}HELTEC_V1\0\u{1}LILYGO_TBEAM_S3_CORE\0\u{1}RAK11200\0\u{1}NANO_G1\0\u{1}TLORA_V2_1_1P8\0\u{1}TLORA_T3_S3\0\u{1}NANO_G1_EXPLORER\0\u{1}NANO_G2_ULTRA\0\u{1}LORA_TYPE\0\u{1}WIPHONE\0\u{1}WIO_WM1110\0\u{1}RAK2560\0\u{1}HELTEC_HRU_3601\0\u{1}HELTEC_WIRELESS_BRIDGE\0\u{1}STATION_G1\0\u{1}RAK11310\0\u{1}SENSELORA_RP2040\0\u{1}SENSELORA_S3\0\u{1}CANARYONE\0\u{1}RP2040_LORA\0\u{1}STATION_G2\0\u{1}LORA_RELAY_V1\0\u{1}T_ECHO_PLUS\0\u{1}PPR\0\u{1}GENIEBLOCKS\0\u{1}NRF52_UNKNOWN\0\u{1}PORTDUINO\0\u{1}ANDROID_SIM\0\u{1}DIY_V1\0\u{1}NRF52840_PCA10059\0\u{1}DR_DEV\0\u{1}M5STACK\0\u{1}HELTEC_V3\0\u{1}HELTEC_WSL_V3\0\u{1}BETAFPV_2400_TX\0\u{1}BETAFPV_900_NANO_TX\0\u{1}RPI_PICO\0\u{1}HELTEC_WIRELESS_TRACKER\0\u{1}HELTEC_WIRELESS_PAPER\0\u{1}T_DECK\0\u{1}T_WATCH_S3\0\u{1}PICOMPUTER_S3\0\u{1}HELTEC_HT62\0\u{1}EBYTE_ESP32_S3\0\u{1}ESP32_S3_PICO\0\u{1}CHATTER_2\0\u{1}HELTEC_WIRELESS_PAPER_V1_0\0\u{1}HELTEC_WIRELESS_TRACKER_V1_0\0\u{1}UNPHONE\0\u{1}TD_LORAC\0\u{1}CDEBYTE_EORA_S3\0\u{1}TWC_MESH_V4\0\u{1}NRF52_PROMICRO_DIY\0\u{1}RADIOMASTER_900_BANDIT_NANO\0\u{1}HELTEC_CAPSULE_SENSOR_V3\0\u{1}HELTEC_VISION_MASTER_T190\0\u{1}HELTEC_VISION_MASTER_E213\0\u{1}HELTEC_VISION_MASTER_E290\0\u{1}HELTEC_MESH_NODE_T114\0\u{1}SENSECAP_INDICATOR\0\u{1}TRACKER_T1000_E\0\u{1}RAK3172\0\u{1}WIO_E5\0\u{1}RADIOMASTER_900_BANDIT\0\u{1}ME25LS01_4Y10TD\0\u{1}RP2040_FEATHER_RFM95\0\u{1}M5STACK_COREBASIC\0\u{1}M5STACK_CORE2\0\u{1}RPI_PICO2\0\u{1}M5STACK_CORES3\0\u{1}SEEED_XIAO_S3\0\u{1}MS24SF1\0\u{1}TLORA_C6\0\u{1}WISMESH_TAP\0\u{1}ROUTASTIC\0\u{1}MESH_TAB\0\u{1}MESHLINK\0\u{1}XIAO_NRF52_KIT\0\u{1}THINKNODE_M1\0\u{1}THINKNODE_M2\0\u{1}T_ETH_ELITE\0\u{1}HELTEC_SENSOR_HUB\0\u{1}MUZI_BASE\0\u{1}HELTEC_MESH_POCKET\0\u{1}SEEED_SOLAR_NODE\0\u{1}NOMADSTAR_METEOR_PRO\0\u{1}CROWPANEL\0\u{1}LINK_32\0\u{1}SEEED_WIO_TRACKER_L1\0\u{1}SEEED_WIO_TRACKER_L1_EINK\0\u{1}MUZI_R1_NEO\0\u{1}T_DECK_PRO\0\u{1}T_LORA_PAGER\0\u{1}M5STACK_RESERVED\0\u{1}WISMESH_TAG\0\u{1}RAK3312\0\u{1}THINKNODE_M5\0\u{1}HELTEC_MESH_SOLAR\0\u{1}T_ECHO_LITE\0\u{1}HELTEC_V4\0\u{1}M5STACK_C6L\0\u{1}M5STACK_CARDPUTER_ADV\0\u{1}HELTEC_WIRELESS_TRACKER_V2\0\u{1}T_WATCH_ULTRA\0\u{1}THINKNODE_M3\0\u{1}WISMESH_TAP_V2\0\u{1}RAK3401\0\u{1}RAK6421\0\u{1}THINKNODE_M4\0\u{1}THINKNODE_M6\0\u{1}MESHSTICK_1262\0\u{1}TBEAM_1_WATT\0\u{1}T5_S3_EPAPER_PRO\0\u{2}D\u{2}PRIVATE_HW\0")
|
||||
}
|
||||
|
||||
extension Constants: SwiftProtobuf._ProtoNameProviding {
|
||||
|
|
@ -4409,7 +4450,7 @@ extension Routing: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBa
|
|||
}
|
||||
|
||||
extension Routing.Error: SwiftProtobuf._ProtoNameProviding {
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0NONE\0\u{1}NO_ROUTE\0\u{1}GOT_NAK\0\u{1}TIMEOUT\0\u{1}NO_INTERFACE\0\u{1}MAX_RETRANSMIT\0\u{1}NO_CHANNEL\0\u{1}TOO_LARGE\0\u{1}NO_RESPONSE\0\u{1}DUTY_CYCLE_LIMIT\0\u{2}\u{17}BAD_REQUEST\0\u{1}NOT_AUTHORIZED\0\u{1}PKI_FAILED\0\u{1}PKI_UNKNOWN_PUBKEY\0\u{1}ADMIN_BAD_SESSION_KEY\0\u{1}ADMIN_PUBLIC_KEY_UNAUTHORIZED\0\u{1}RATE_LIMIT_EXCEEDED\0")
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0NONE\0\u{1}NO_ROUTE\0\u{1}GOT_NAK\0\u{1}TIMEOUT\0\u{1}NO_INTERFACE\0\u{1}MAX_RETRANSMIT\0\u{1}NO_CHANNEL\0\u{1}TOO_LARGE\0\u{1}NO_RESPONSE\0\u{1}DUTY_CYCLE_LIMIT\0\u{2}\u{17}BAD_REQUEST\0\u{1}NOT_AUTHORIZED\0\u{1}PKI_FAILED\0\u{1}PKI_UNKNOWN_PUBKEY\0\u{1}ADMIN_BAD_SESSION_KEY\0\u{1}ADMIN_PUBLIC_KEY_UNAUTHORIZED\0\u{1}RATE_LIMIT_EXCEEDED\0\u{1}PKI_SEND_FAIL_PUBLIC_KEY\0")
|
||||
}
|
||||
|
||||
extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
|
|
@ -4528,7 +4569,7 @@ extension KeyVerification: SwiftProtobuf.Message, SwiftProtobuf._MessageImplemen
|
|||
|
||||
extension StoreForwardPlusPlus: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = _protobuf_package + ".StoreForwardPlusPlus"
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}sfpp_message_type\0\u{3}message_hash\0\u{3}commit_hash\0\u{3}root_hash\0\u{1}message\0\u{3}encapsulated_id\0\u{3}encapsulated_to\0\u{3}encapsulated_from\0\u{3}encapsulated_rxtime\0")
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}sfpp_message_type\0\u{3}message_hash\0\u{3}commit_hash\0\u{3}root_hash\0\u{1}message\0\u{3}encapsulated_id\0\u{3}encapsulated_to\0\u{3}encapsulated_from\0\u{3}encapsulated_rxtime\0\u{3}chain_count\0")
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
|
|
@ -4545,6 +4586,7 @@ extension StoreForwardPlusPlus: SwiftProtobuf.Message, SwiftProtobuf._MessageImp
|
|||
case 7: try { try decoder.decodeSingularUInt32Field(value: &self.encapsulatedTo) }()
|
||||
case 8: try { try decoder.decodeSingularUInt32Field(value: &self.encapsulatedFrom) }()
|
||||
case 9: try { try decoder.decodeSingularUInt32Field(value: &self.encapsulatedRxtime) }()
|
||||
case 10: try { try decoder.decodeSingularUInt32Field(value: &self.chainCount) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -4578,6 +4620,9 @@ extension StoreForwardPlusPlus: SwiftProtobuf.Message, SwiftProtobuf._MessageImp
|
|||
if self.encapsulatedRxtime != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.encapsulatedRxtime, fieldNumber: 9)
|
||||
}
|
||||
if self.chainCount != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.chainCount, fieldNumber: 10)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
|
|
@ -4591,6 +4636,7 @@ extension StoreForwardPlusPlus: SwiftProtobuf.Message, SwiftProtobuf._MessageImp
|
|||
if lhs.encapsulatedTo != rhs.encapsulatedTo {return false}
|
||||
if lhs.encapsulatedFrom != rhs.encapsulatedFrom {return false}
|
||||
if lhs.encapsulatedRxtime != rhs.encapsulatedRxtime {return false}
|
||||
if lhs.chainCount != rhs.chainCount {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
|
|
@ -4981,7 +5027,7 @@ extension MeshPacket.TransportMechanism: SwiftProtobuf._ProtoNameProviding {
|
|||
|
||||
extension NodeInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = _protobuf_package + ".NodeInfo"
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}num\0\u{1}user\0\u{1}position\0\u{1}snr\0\u{3}last_heard\0\u{3}device_metrics\0\u{1}channel\0\u{3}via_mqtt\0\u{3}hops_away\0\u{3}is_favorite\0\u{3}is_ignored\0\u{3}is_key_manually_verified\0")
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}num\0\u{1}user\0\u{1}position\0\u{1}snr\0\u{3}last_heard\0\u{3}device_metrics\0\u{1}channel\0\u{3}via_mqtt\0\u{3}hops_away\0\u{3}is_favorite\0\u{3}is_ignored\0\u{3}is_key_manually_verified\0\u{3}is_muted\0")
|
||||
|
||||
fileprivate class _StorageClass {
|
||||
var _num: UInt32 = 0
|
||||
|
|
@ -4996,6 +5042,7 @@ extension NodeInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
|
|||
var _isFavorite: Bool = false
|
||||
var _isIgnored: Bool = false
|
||||
var _isKeyManuallyVerified: Bool = false
|
||||
var _isMuted: Bool = false
|
||||
|
||||
// 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.
|
||||
|
|
@ -5018,6 +5065,7 @@ extension NodeInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
|
|||
_isFavorite = source._isFavorite
|
||||
_isIgnored = source._isIgnored
|
||||
_isKeyManuallyVerified = source._isKeyManuallyVerified
|
||||
_isMuted = source._isMuted
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -5048,6 +5096,7 @@ extension NodeInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
|
|||
case 10: try { try decoder.decodeSingularBoolField(value: &_storage._isFavorite) }()
|
||||
case 11: try { try decoder.decodeSingularBoolField(value: &_storage._isIgnored) }()
|
||||
case 12: try { try decoder.decodeSingularBoolField(value: &_storage._isKeyManuallyVerified) }()
|
||||
case 13: try { try decoder.decodeSingularBoolField(value: &_storage._isMuted) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -5096,6 +5145,9 @@ extension NodeInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
|
|||
if _storage._isKeyManuallyVerified != false {
|
||||
try visitor.visitSingularBoolField(value: _storage._isKeyManuallyVerified, fieldNumber: 12)
|
||||
}
|
||||
if _storage._isMuted != false {
|
||||
try visitor.visitSingularBoolField(value: _storage._isMuted, fieldNumber: 13)
|
||||
}
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
|
@ -5117,6 +5169,7 @@ extension NodeInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
|
|||
if _storage._isFavorite != rhs_storage._isFavorite {return false}
|
||||
if _storage._isIgnored != rhs_storage._isIgnored {return false}
|
||||
if _storage._isKeyManuallyVerified != rhs_storage._isKeyManuallyVerified {return false}
|
||||
if _storage._isMuted != rhs_storage._isMuted {return false}
|
||||
return true
|
||||
}
|
||||
if !storagesAreEqual {return false}
|
||||
|
|
|
|||
|
|
@ -805,9 +805,15 @@ public struct ModuleConfig: Sendable {
|
|||
/// https://beta.ivc.no/wiki/index.php/Victron_VE_Direct_DIY_Cable
|
||||
case veDirect // = 7
|
||||
|
||||
///Used to configure and view some parameters of MeshSolar.
|
||||
///https://heltec.org/project/meshsolar/
|
||||
/// Used to configure and view some parameters of MeshSolar.
|
||||
/// https://heltec.org/project/meshsolar/
|
||||
case msConfig // = 8
|
||||
|
||||
/// Logs mesh traffic to the serial pins, ideal for logging via openLog or similar.
|
||||
case log // = 9
|
||||
|
||||
/// only text (channel & DM)
|
||||
case logtext // = 10
|
||||
case UNRECOGNIZED(Int)
|
||||
|
||||
public init() {
|
||||
|
|
@ -825,6 +831,8 @@ public struct ModuleConfig: Sendable {
|
|||
case 6: self = .ws85
|
||||
case 7: self = .veDirect
|
||||
case 8: self = .msConfig
|
||||
case 9: self = .log
|
||||
case 10: self = .logtext
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
}
|
||||
}
|
||||
|
|
@ -840,6 +848,8 @@ public struct ModuleConfig: Sendable {
|
|||
case .ws85: return 6
|
||||
case .veDirect: return 7
|
||||
case .msConfig: return 8
|
||||
case .log: return 9
|
||||
case .logtext: return 10
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
}
|
||||
}
|
||||
|
|
@ -855,6 +865,8 @@ public struct ModuleConfig: Sendable {
|
|||
.ws85,
|
||||
.veDirect,
|
||||
.msConfig,
|
||||
.log,
|
||||
.logtext,
|
||||
]
|
||||
|
||||
}
|
||||
|
|
@ -1080,6 +1092,10 @@ public struct ModuleConfig: Sendable {
|
|||
/// Note: We will still send telemtry to the connected phone / client every minute over the API
|
||||
public var deviceTelemetryEnabled: Bool = false
|
||||
|
||||
///
|
||||
/// Enable/Disable the air quality telemetry measurement module on-device display
|
||||
public var airQualityScreenEnabled: Bool = false
|
||||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
public init() {}
|
||||
|
|
@ -2005,7 +2021,7 @@ extension ModuleConfig.SerialConfig.Serial_Baud: SwiftProtobuf._ProtoNameProvidi
|
|||
}
|
||||
|
||||
extension ModuleConfig.SerialConfig.Serial_Mode: SwiftProtobuf._ProtoNameProviding {
|
||||
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")
|
||||
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\u{1}LOG\0\u{1}LOGTEXT\0")
|
||||
}
|
||||
|
||||
extension ModuleConfig.ExternalNotificationConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
|
|
@ -2210,7 +2226,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(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 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\u{3}air_quality_screen_enabled\0")
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
|
|
@ -2232,6 +2248,7 @@ extension ModuleConfig.TelemetryConfig: SwiftProtobuf.Message, SwiftProtobuf._Me
|
|||
case 12: try { try decoder.decodeSingularUInt32Field(value: &self.healthUpdateInterval) }()
|
||||
case 13: try { try decoder.decodeSingularBoolField(value: &self.healthScreenEnabled) }()
|
||||
case 14: try { try decoder.decodeSingularBoolField(value: &self.deviceTelemetryEnabled) }()
|
||||
case 15: try { try decoder.decodeSingularBoolField(value: &self.airQualityScreenEnabled) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -2280,6 +2297,9 @@ extension ModuleConfig.TelemetryConfig: SwiftProtobuf.Message, SwiftProtobuf._Me
|
|||
if self.deviceTelemetryEnabled != false {
|
||||
try visitor.visitSingularBoolField(value: self.deviceTelemetryEnabled, fieldNumber: 14)
|
||||
}
|
||||
if self.airQualityScreenEnabled != false {
|
||||
try visitor.visitSingularBoolField(value: self.airQualityScreenEnabled, fieldNumber: 15)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
|
|
@ -2298,6 +2318,7 @@ extension ModuleConfig.TelemetryConfig: SwiftProtobuf.Message, SwiftProtobuf._Me
|
|||
if lhs.healthUpdateInterval != rhs.healthUpdateInterval {return false}
|
||||
if lhs.healthScreenEnabled != rhs.healthScreenEnabled {return false}
|
||||
if lhs.deviceTelemetryEnabled != rhs.deviceTelemetryEnabled {return false}
|
||||
if lhs.airQualityScreenEnabled != rhs.airQualityScreenEnabled {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 62ef17b3d1625fc6d78ed661f614d0baad4be9ef
|
||||
Subproject commit c8d5047b6351b732c0bccfcea6960a532f7ae49a
|
||||
Loading…
Add table
Add a link
Reference in a new issue