From 7e67532f24c4bb5637c1f3d55bf2d3a6524af338 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sat, 20 Apr 2024 23:37:14 -0700 Subject: [PATCH 1/7] Add power off to connect context menu --- Meshtastic/Views/Bluetooth/Connect.swift | 10 +++++++++- protobufs | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index da0b2a9e..201ec969 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -121,6 +121,14 @@ struct Connect: View { Text("Short Name: \(node?.user?.shortName ?? "?")") Text("Long Name: \(node?.user?.longName ?? "unknown".localized)") Text("BLE RSSI: \(bleManager.connectedPeripheral.rssi)") + Button { + if !bleManager.sendShutdown(fromUser: node!.user!, toUser: node!.user!, adminIndex: node!.myInfo!.adminIndex) { + print("Shutdown Failed") + } + + } label: { + Label("Power Off", systemImage: "power") + } } } if isUnsetRegion { @@ -234,7 +242,7 @@ struct Connect: View { } .textCase(nil) } - + } else { Text("bluetooth.off") .foregroundColor(.red) diff --git a/protobufs b/protobufs index 22cbd0d4..f92900c5 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 22cbd0d4cfafa4b8c1e64517e06edc2d7a22cca9 +Subproject commit f92900c5f884b04388fb7abf61d4df66783015e4 From 205451193610256a75acb9b926f533c5bc7085c0 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 21 Apr 2024 10:46:17 -0700 Subject: [PATCH 2/7] Update message details --- .../Views/Messages/MessageContextMenuItems.swift | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Meshtastic/Views/Messages/MessageContextMenuItems.swift b/Meshtastic/Views/Messages/MessageContextMenuItems.swift index d98eaeec..4fa31421 100644 --- a/Meshtastic/Views/Messages/MessageContextMenuItems.swift +++ b/Meshtastic/Views/Messages/MessageContextMenuItems.swift @@ -53,9 +53,14 @@ struct MessageContextMenuItems: View { let messageDate = Date(timeIntervalSince1970: TimeInterval(message.messageTimestamp)) Text("\(messageDate.formattedDate(format: MessageText.dateFormatString))").foregroundColor(.gray) } - if !isCurrentUser { + 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") + } + } else if !isCurrentUser && !(message.fromUser?.userNode?.viaMqtt ?? false) { + VStack { + Text("Hops Away \(message.fromUser?.userNode?.hopsAway ?? 0)) dB") } } if isCurrentUser && message.receivedACK { @@ -78,10 +83,6 @@ struct MessageContextMenuItems: View { if ackDate >= sixMonthsAgo! { Text("Ack Time: \(ackDate.formattedDate(format: "h:mm:ss.SSSS a"))") .foregroundColor(.gray) - } else { - Text("unknown.age") - .font(.caption2) - .foregroundColor(.gray) } } } From d780f103ad4b2ae504340e0b5ad6017da3e01ba6 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 21 Apr 2024 20:36:29 -0700 Subject: [PATCH 3/7] Set provide location default to 30 seconds Don't delete routes unless doing a factory reset Close route recorder modal when finished --- Meshtastic/Extensions/UserDefaults.swift | 2 +- Meshtastic/Persistence/UpdateCoreData.swift | 16 +++++++++++++--- Meshtastic/Views/Bluetooth/Connect.swift | 2 +- Meshtastic/Views/Settings/AppSettings.swift | 2 +- .../Views/Settings/Config/DeviceConfig.swift | 4 ++-- Meshtastic/Views/Settings/RouteRecorder.swift | 1 + 6 files changed, 19 insertions(+), 8 deletions(-) diff --git a/Meshtastic/Extensions/UserDefaults.swift b/Meshtastic/Extensions/UserDefaults.swift index 1a3e05b2..49757d69 100644 --- a/Meshtastic/Extensions/UserDefaults.swift +++ b/Meshtastic/Extensions/UserDefaults.swift @@ -86,7 +86,7 @@ extension UserDefaults { @UserDefault(.provideLocation, defaultValue: false) static var provideLocation: Bool - @UserDefault(.provideLocationInterval, defaultValue: 0) + @UserDefault(.provideLocationInterval, defaultValue: 30) static var provideLocationInterval: Int @UserDefault(.mapLayer, defaultValue: .standard) diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index 90a35cce..0b5428fc 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -106,14 +106,24 @@ public func deleteUserMessages(user: UserEntity, context: NSManagedObjectContext } } -public func clearCoreDataDatabase(context: NSManagedObjectContext) { +public func clearCoreDataDatabase(context: NSManagedObjectContext, includeRoutes: Bool) { let persistenceController = PersistenceController.shared.container for i in 0...persistenceController.managedObjectModel.entities.count-1 { + let entity = persistenceController.managedObjectModel.entities[i] let query = NSFetchRequest(entityName: entity.name!) - let deleteRequest = NSBatchDeleteRequest(fetchRequest: query) - + var deleteRequest = NSBatchDeleteRequest(fetchRequest: query) + let entityName = entity.name ?? "UNK" + + if includeRoutes { + deleteRequest = NSBatchDeleteRequest(fetchRequest: query) + } else if !includeRoutes { + if !(entityName.contains("RouteEntity") || entityName.contains("LocationEntity")) { + print(entity.name?.lowercased()) + deleteRequest = NSBatchDeleteRequest(fetchRequest: query) + } + } do { try context.executeAndMergeChanges(using: deleteRequest) } catch let error as NSError { diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index 201ec969..498496ce 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -232,7 +232,7 @@ struct Connect: View { if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == CBPeripheralState.connected { bleManager.disconnectPeripheral() } - clearCoreDataDatabase(context: context) + clearCoreDataDatabase(context: context, includeRoutes: false) let radio = bleManager.peripherals.first(where: { $0.peripheral.identifier.uuidString == selectedPeripherialId }) if radio != nil { diff --git a/Meshtastic/Views/Settings/AppSettings.swift b/Meshtastic/Views/Settings/AppSettings.swift index e614153a..ba5d46f9 100644 --- a/Meshtastic/Views/Settings/AppSettings.swift +++ b/Meshtastic/Views/Settings/AppSettings.swift @@ -75,7 +75,7 @@ struct AppSettings: View { ) { Button("Erase all app data?", role: .destructive) { bleManager.disconnectPeripheral() - clearCoreDataDatabase(context: context) + clearCoreDataDatabase(context: context, includeRoutes: true) context.refreshAllObjects() UserDefaults.standard.reset() } diff --git a/Meshtastic/Views/Settings/Config/DeviceConfig.swift b/Meshtastic/Views/Settings/Config/DeviceConfig.swift index 68596dab..1d438cbe 100644 --- a/Meshtastic/Views/Settings/Config/DeviceConfig.swift +++ b/Meshtastic/Views/Settings/Config/DeviceConfig.swift @@ -153,7 +153,7 @@ struct DeviceConfig: View { if bleManager.sendNodeDBReset(fromUser: node!.user!, toUser: node!.user!) { DispatchQueue.main.asyncAfter(deadline: .now() + 1) { bleManager.disconnectPeripheral() - clearCoreDataDatabase(context: context) + clearCoreDataDatabase(context: context, includeRoutes: false) } } else { @@ -178,7 +178,7 @@ struct DeviceConfig: View { if bleManager.sendFactoryReset(fromUser: node!.user!, toUser: node!.user!) { DispatchQueue.main.asyncAfter(deadline: .now() + 1) { bleManager.disconnectPeripheral() - clearCoreDataDatabase(context: context) + clearCoreDataDatabase(context: context, includeRoutes: false) } } else { print("Factory Reset Failed") diff --git a/Meshtastic/Views/Settings/RouteRecorder.swift b/Meshtastic/Views/Settings/RouteRecorder.swift index 63ca1697..766b72d5 100644 --- a/Meshtastic/Views/Settings/RouteRecorder.swift +++ b/Meshtastic/Views/Settings/RouteRecorder.swift @@ -248,6 +248,7 @@ struct RouteRecorder: View { let nsError = error as NSError print("💥 Error Saving RouteEntity from the Route Recorder \(nsError)") } + isShowingDetails = false } label: { Label("finish", systemImage: "flag.checkered") } From df75d28ade22a93c713633e29d9343cd88fff6eb Mon Sep 17 00:00:00 2001 From: Benjamin Faershtein <119711889+RCGV1@users.noreply.github.com> Date: Thu, 25 Apr 2024 08:54:24 -0700 Subject: [PATCH 4/7] Fixed typo --- Meshtastic/Views/Settings/Firmware.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Meshtastic/Views/Settings/Firmware.swift b/Meshtastic/Views/Settings/Firmware.swift index f12ba7db..2c03e2a3 100644 --- a/Meshtastic/Views/Settings/Firmware.swift +++ b/Meshtastic/Views/Settings/Firmware.swift @@ -152,7 +152,7 @@ struct Firmware: View { Link("Web Flasher", destination: URL(string: "https://flash.meshtastic.org")!) .font(.callout) .padding(.bottom) - Text("ESP 32 OTA update is a work in progress, click the button below to sent your device a reboot into ota admin message.") + Text("ESP 32 OTA update is a work in progress, click the button below to send your device a reboot into ota admin message.") .font(.caption) HStack(alignment: .center) { Spacer() From 1957d761da3aa4233eb622eac1c1fa0335ec1cf2 Mon Sep 17 00:00:00 2001 From: nagumii <40807970+nagumii@users.noreply.github.com> Date: Fri, 26 Apr 2024 16:12:05 +0300 Subject: [PATCH 5/7] Fixed typo on firmware update screen --- Meshtastic/Views/Settings/Firmware.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Meshtastic/Views/Settings/Firmware.swift b/Meshtastic/Views/Settings/Firmware.swift index f12ba7db..6d0bafc7 100644 --- a/Meshtastic/Views/Settings/Firmware.swift +++ b/Meshtastic/Views/Settings/Firmware.swift @@ -94,7 +94,7 @@ struct Firmware: View { if currentDevice?.architecture == Meshtastic.Architecture.nrf52840 { VStack(alignment: .leading) { - Text("Drag & Drop is the reccomended way to update firmware for NRF devices. If your iPhone or iPad is USB-C it will work with your regular USB-C charging cable, for lightning devices you need the Apple Lightning to USB camera adaptor.") + Text("Drag & Drop is the recommended way to update firmware for NRF devices. If your iPhone or iPad is USB-C it will work with your regular USB-C charging cable, for lightning devices you need the Apple Lightning to USB camera adaptor.") .fixedSize(horizontal: false, vertical: true) .foregroundStyle(.gray) .font(.caption) From f13efa8179a434c84719b60ad4517484558fe746 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sat, 27 Apr 2024 20:47:06 -0700 Subject: [PATCH 6/7] Proto updates --- .../Protobufs/meshtastic/admin.pb.swift | 4 +- .../Protobufs/meshtastic/apponly.pb.swift | 10 +-- Meshtastic/Protobufs/meshtastic/atak.pb.swift | 4 +- .../Protobufs/meshtastic/channel.pb.swift | 2 +- .../Protobufs/meshtastic/config.pb.swift | 63 +++++++++------- .../Protobufs/meshtastic/deviceonly.pb.swift | 22 +----- .../Protobufs/meshtastic/localonly.pb.swift | 20 +---- Meshtastic/Protobufs/meshtastic/mesh.pb.swift | 75 ++++++++----------- .../meshtastic/module_config.pb.swift | 10 +-- .../Protobufs/meshtastic/portnums.pb.swift | 2 +- .../meshtastic/remote_hardware.pb.swift | 2 +- .../meshtastic/storeforward.pb.swift | 2 +- .../Protobufs/meshtastic/telemetry.pb.swift | 20 ++++- .../Protobufs/meshtastic/xmodem.pb.swift | 2 +- protobufs | 2 +- 15 files changed, 104 insertions(+), 136 deletions(-) diff --git a/Meshtastic/Protobufs/meshtastic/admin.pb.swift b/Meshtastic/Protobufs/meshtastic/admin.pb.swift index 230f5304..1f2b193e 100644 --- a/Meshtastic/Protobufs/meshtastic/admin.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/admin.pb.swift @@ -924,7 +924,7 @@ struct AdminMessage { extension AdminMessage.ConfigType: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [AdminMessage.ConfigType] = [ + static var allCases: [AdminMessage.ConfigType] = [ .deviceConfig, .positionConfig, .powerConfig, @@ -937,7 +937,7 @@ extension AdminMessage.ConfigType: CaseIterable { extension AdminMessage.ModuleConfigType: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [AdminMessage.ModuleConfigType] = [ + static var allCases: [AdminMessage.ModuleConfigType] = [ .mqttConfig, .serialConfig, .extnotifConfig, diff --git a/Meshtastic/Protobufs/meshtastic/apponly.pb.swift b/Meshtastic/Protobufs/meshtastic/apponly.pb.swift index 11abf7af..ffce4849 100644 --- a/Meshtastic/Protobufs/meshtastic/apponly.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/apponly.pb.swift @@ -75,15 +75,7 @@ extension ChannelSet: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio var _settings: [ChannelSettings] = [] var _loraConfig: Config.LoRaConfig? = 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 + static let defaultInstance = _StorageClass() private init() {} diff --git a/Meshtastic/Protobufs/meshtastic/atak.pb.swift b/Meshtastic/Protobufs/meshtastic/atak.pb.swift index 77c35e7c..f1bc14ad 100644 --- a/Meshtastic/Protobufs/meshtastic/atak.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/atak.pb.swift @@ -136,7 +136,7 @@ enum Team: SwiftProtobuf.Enum { extension Team: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [Team] = [ + static var allCases: [Team] = [ .unspecifedColor, .white, .yellow, @@ -239,7 +239,7 @@ enum MemberRole: SwiftProtobuf.Enum { extension MemberRole: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [MemberRole] = [ + static var allCases: [MemberRole] = [ .unspecifed, .teamMember, .teamLead, diff --git a/Meshtastic/Protobufs/meshtastic/channel.pb.swift b/Meshtastic/Protobufs/meshtastic/channel.pb.swift index b2c55540..493230ba 100644 --- a/Meshtastic/Protobufs/meshtastic/channel.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/channel.pb.swift @@ -215,7 +215,7 @@ struct Channel { extension Channel.Role: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [Channel.Role] = [ + static var allCases: [Channel.Role] = [ .disabled, .primary, .secondary, diff --git a/Meshtastic/Protobufs/meshtastic/config.pb.swift b/Meshtastic/Protobufs/meshtastic/config.pb.swift index db2b7f04..cf1077e5 100644 --- a/Meshtastic/Protobufs/meshtastic/config.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/config.pb.swift @@ -194,6 +194,10 @@ struct Config { /// POSIX Timezone definition string from https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv. var tzdef: String = String() + /// + /// If true, disable the default blinking LED (LED_PIN) behavior on the device + var ledHeartbeatDisabled: Bool = false + var unknownFields = SwiftProtobuf.UnknownStorage() /// @@ -584,27 +588,25 @@ struct Config { // methods supported on all messages. /// - /// If set, we are powered from a low-current source (i.e. solar), so even if it looks like we have power flowing in - /// we should try to minimize power consumption as much as possible. - /// YOU DO NOT NEED TO SET THIS IF YOU'VE set is_router (it is implied in that case). - /// Advanced Option + /// Description: Will sleep everything as much as possible, for the tracker and sensor role this will also include the lora radio. + /// Don't use this setting if you want to use your device with the phone apps or are using a device without a user button. + /// Technical Details: Works for ESP32 devices and NRF52 devices in the Sensor or Tracker roles var isPowerSaving: Bool = false /// - /// If non-zero, the device will fully power off this many seconds after external power is removed. + /// Description: If non-zero, the device will fully power off this many seconds after external power is removed. var onBatteryShutdownAfterSecs: UInt32 = 0 /// /// Ratio of voltage divider for battery pin eg. 3.20 (R1=100k, R2=220k) /// Overrides the ADC_MULTIPLIER defined in variant for battery voltage calculation. - /// Should be set to floating point value between 2 and 4 - /// Fixes issues on Heltec v2 + /// https://meshtastic.org/docs/configuration/radio/power/#adc-multiplier-override + /// Should be set to floating point value between 2 and 6 var adcMultiplierOverride: Float = 0 /// - /// Wait Bluetooth Seconds - /// The number of seconds for to wait before turning off BLE in No Bluetooth states - /// 0 for default of 1 minute + /// Description: The number of seconds for to wait before turning off BLE in No Bluetooth states + /// Technical Details: ESP32 Only 0 for default of 1 minute var waitBluetoothSecs: UInt32 = 0 /// @@ -615,16 +617,13 @@ struct Config { var sdsSecs: UInt32 = 0 /// - /// Light Sleep Seconds - /// In light sleep the CPU is suspended, LoRa radio is on, BLE is off an GPS is on - /// ESP32 Only - /// 0 for default of 300 + /// Description: In light sleep the CPU is suspended, LoRa radio is on, BLE is off an GPS is on + /// Technical Details: ESP32 Only 0 for default of 300 var lsSecs: UInt32 = 0 /// - /// Minimum Wake Seconds - /// While in light sleep when we receive packets on the LoRa radio we will wake and handle them and stay awake in no BLE mode for this value - /// 0 for default of 10 seconds + /// Description: While in light sleep when we receive packets on the LoRa radio we will wake and handle them and stay awake in no BLE mode for this value + /// Technical Details: ESP32 Only 0 for default of 10 seconds var minWakeSecs: UInt32 = 0 /// @@ -1387,7 +1386,7 @@ struct Config { extension Config.DeviceConfig.Role: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [Config.DeviceConfig.Role] = [ + static var allCases: [Config.DeviceConfig.Role] = [ .client, .clientMute, .router, @@ -1404,7 +1403,7 @@ extension Config.DeviceConfig.Role: CaseIterable { extension Config.DeviceConfig.RebroadcastMode: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [Config.DeviceConfig.RebroadcastMode] = [ + static var allCases: [Config.DeviceConfig.RebroadcastMode] = [ .all, .allSkipDecoding, .localOnly, @@ -1414,7 +1413,7 @@ extension Config.DeviceConfig.RebroadcastMode: CaseIterable { extension Config.PositionConfig.PositionFlags: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [Config.PositionConfig.PositionFlags] = [ + static var allCases: [Config.PositionConfig.PositionFlags] = [ .unset, .altitude, .altitudeMsl, @@ -1431,7 +1430,7 @@ extension Config.PositionConfig.PositionFlags: CaseIterable { extension Config.PositionConfig.GpsMode: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [Config.PositionConfig.GpsMode] = [ + static var allCases: [Config.PositionConfig.GpsMode] = [ .disabled, .enabled, .notPresent, @@ -1440,7 +1439,7 @@ extension Config.PositionConfig.GpsMode: CaseIterable { extension Config.NetworkConfig.AddressMode: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [Config.NetworkConfig.AddressMode] = [ + static var allCases: [Config.NetworkConfig.AddressMode] = [ .dhcp, .static, ] @@ -1448,7 +1447,7 @@ extension Config.NetworkConfig.AddressMode: CaseIterable { extension Config.DisplayConfig.GpsCoordinateFormat: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [Config.DisplayConfig.GpsCoordinateFormat] = [ + static var allCases: [Config.DisplayConfig.GpsCoordinateFormat] = [ .dec, .dms, .utm, @@ -1460,7 +1459,7 @@ extension Config.DisplayConfig.GpsCoordinateFormat: CaseIterable { extension Config.DisplayConfig.DisplayUnits: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [Config.DisplayConfig.DisplayUnits] = [ + static var allCases: [Config.DisplayConfig.DisplayUnits] = [ .metric, .imperial, ] @@ -1468,7 +1467,7 @@ extension Config.DisplayConfig.DisplayUnits: CaseIterable { extension Config.DisplayConfig.OledType: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [Config.DisplayConfig.OledType] = [ + static var allCases: [Config.DisplayConfig.OledType] = [ .oledAuto, .oledSsd1306, .oledSh1106, @@ -1478,7 +1477,7 @@ extension Config.DisplayConfig.OledType: CaseIterable { extension Config.DisplayConfig.DisplayMode: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [Config.DisplayConfig.DisplayMode] = [ + static var allCases: [Config.DisplayConfig.DisplayMode] = [ .default, .twocolor, .inverted, @@ -1488,7 +1487,7 @@ extension Config.DisplayConfig.DisplayMode: CaseIterable { extension Config.LoRaConfig.RegionCode: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [Config.LoRaConfig.RegionCode] = [ + static var allCases: [Config.LoRaConfig.RegionCode] = [ .unset, .us, .eu433, @@ -1513,7 +1512,7 @@ extension Config.LoRaConfig.RegionCode: CaseIterable { extension Config.LoRaConfig.ModemPreset: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [Config.LoRaConfig.ModemPreset] = [ + static var allCases: [Config.LoRaConfig.ModemPreset] = [ .longFast, .longSlow, .veryLongSlow, @@ -1527,7 +1526,7 @@ extension Config.LoRaConfig.ModemPreset: CaseIterable { extension Config.BluetoothConfig.PairingMode: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [Config.BluetoothConfig.PairingMode] = [ + static var allCases: [Config.BluetoothConfig.PairingMode] = [ .randomPin, .fixedPin, .noPin, @@ -1739,6 +1738,7 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl 9: .standard(proto: "is_managed"), 10: .standard(proto: "disable_triple_click"), 11: .same(proto: "tzdef"), + 12: .standard(proto: "led_heartbeat_disabled"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1758,6 +1758,7 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl case 9: try { try decoder.decodeSingularBoolField(value: &self.isManaged) }() case 10: try { try decoder.decodeSingularBoolField(value: &self.disableTripleClick) }() case 11: try { try decoder.decodeSingularStringField(value: &self.tzdef) }() + case 12: try { try decoder.decodeSingularBoolField(value: &self.ledHeartbeatDisabled) }() default: break } } @@ -1797,6 +1798,9 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl if !self.tzdef.isEmpty { try visitor.visitSingularStringField(value: self.tzdef, fieldNumber: 11) } + if self.ledHeartbeatDisabled != false { + try visitor.visitSingularBoolField(value: self.ledHeartbeatDisabled, fieldNumber: 12) + } try unknownFields.traverse(visitor: &visitor) } @@ -1812,6 +1816,7 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl if lhs.isManaged != rhs.isManaged {return false} if lhs.disableTripleClick != rhs.disableTripleClick {return false} if lhs.tzdef != rhs.tzdef {return false} + if lhs.ledHeartbeatDisabled != rhs.ledHeartbeatDisabled {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/Meshtastic/Protobufs/meshtastic/deviceonly.pb.swift b/Meshtastic/Protobufs/meshtastic/deviceonly.pb.swift index ebd5e3f7..433bffd3 100644 --- a/Meshtastic/Protobufs/meshtastic/deviceonly.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/deviceonly.pb.swift @@ -66,7 +66,7 @@ enum ScreenFonts: SwiftProtobuf.Enum { extension ScreenFonts: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [ScreenFonts] = [ + static var allCases: [ScreenFonts] = [ .fontSmall, .fontMedium, .fontLarge, @@ -509,15 +509,7 @@ extension NodeInfoLite: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat var _hopsAway: UInt32 = 0 var _isFavorite: Bool = false - #if swift(>=5.10) - // This property is used as the initial default value for new instances of the type. - // The type itself is protecting the reference to its storage via CoW semantics. - // This will force a copy to be made of this reference when the first mutation occurs; - // hence, it is safe to mark this as `nonisolated(unsafe)`. - static nonisolated(unsafe) let defaultInstance = _StorageClass() - #else - static let defaultInstance = _StorageClass() - #endif + static let defaultInstance = _StorageClass() private init() {} @@ -657,15 +649,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati var _nodeRemoteHardwarePins: [NodeRemoteHardwarePin] = [] var _nodeDbLite: [NodeInfoLite] = [] - #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 + static let defaultInstance = _StorageClass() private init() {} diff --git a/Meshtastic/Protobufs/meshtastic/localonly.pb.swift b/Meshtastic/Protobufs/meshtastic/localonly.pb.swift index 0778e962..6e215220 100644 --- a/Meshtastic/Protobufs/meshtastic/localonly.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/localonly.pb.swift @@ -314,15 +314,7 @@ extension LocalConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati var _bluetooth: Config.BluetoothConfig? = 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 + static let defaultInstance = _StorageClass() private init() {} @@ -458,15 +450,7 @@ 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 + static let defaultInstance = _StorageClass() private init() {} diff --git a/Meshtastic/Protobufs/meshtastic/mesh.pb.swift b/Meshtastic/Protobufs/meshtastic/mesh.pb.swift index c9c26377..83c930df 100644 --- a/Meshtastic/Protobufs/meshtastic/mesh.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/mesh.pb.swift @@ -265,6 +265,15 @@ enum HardwareModel: SwiftProtobuf.Enum { /// Compatible with the TD-WRLS development board case tdLorac // = 60 + /// + /// CDEBYTE EoRa-S3 board using their own MM modules, clone of LILYGO T3S3 + case cdebyteEoraS3 // = 61 + + /// + /// TWC_MESH_V4 + /// Adafruit NRF52840 feather express with SX1262, SSD1306 OLED and NEO6M GPS + case twcMeshV4 // = 62 + /// /// ------------------------------------------------------------------------------------------------------------------------------------------ /// 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. @@ -334,6 +343,8 @@ enum HardwareModel: SwiftProtobuf.Enum { case 58: self = .heltecWirelessTrackerV10 case 59: self = .unphone case 60: self = .tdLorac + case 61: self = .cdebyteEoraS3 + case 62: self = .twcMeshV4 case 255: self = .privateHw default: self = .UNRECOGNIZED(rawValue) } @@ -397,6 +408,8 @@ enum HardwareModel: SwiftProtobuf.Enum { case .heltecWirelessTrackerV10: return 58 case .unphone: return 59 case .tdLorac: return 60 + case .cdebyteEoraS3: return 61 + case .twcMeshV4: return 62 case .privateHw: return 255 case .UNRECOGNIZED(let i): return i } @@ -408,7 +421,7 @@ enum HardwareModel: SwiftProtobuf.Enum { extension HardwareModel: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [HardwareModel] = [ + static var allCases: [HardwareModel] = [ .unset, .tloraV2, .tloraV1, @@ -465,6 +478,8 @@ extension HardwareModel: CaseIterable { .heltecWirelessTrackerV10, .unphone, .tdLorac, + .cdebyteEoraS3, + .twcMeshV4, .privateHw, ] } @@ -514,7 +529,7 @@ enum Constants: SwiftProtobuf.Enum { extension Constants: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [Constants] = [ + static var allCases: [Constants] = [ .zero, .dataPayloadLen, ] @@ -627,7 +642,7 @@ enum CriticalErrorCode: SwiftProtobuf.Enum { extension CriticalErrorCode: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [CriticalErrorCode] = [ + static var allCases: [CriticalErrorCode] = [ .none, .txWatchdog, .sleepEnterWait, @@ -946,7 +961,7 @@ struct Position { extension Position.LocSource: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [Position.LocSource] = [ + static var allCases: [Position.LocSource] = [ .locUnset, .locManual, .locInternal, @@ -956,7 +971,7 @@ extension Position.LocSource: CaseIterable { extension Position.AltSource: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [Position.AltSource] = [ + static var allCases: [Position.AltSource] = [ .altUnset, .altManual, .altInternal, @@ -1237,7 +1252,7 @@ struct Routing { extension Routing.Error: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [Routing.Error] = [ + static var allCases: [Routing.Error] = [ .none, .noRoute, .gotNak, @@ -1755,7 +1770,7 @@ struct MeshPacket { extension MeshPacket.Priority: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [MeshPacket.Priority] = [ + static var allCases: [MeshPacket.Priority] = [ .unset, .min, .background, @@ -1768,7 +1783,7 @@ extension MeshPacket.Priority: CaseIterable { extension MeshPacket.Delayed: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [MeshPacket.Delayed] = [ + static var allCases: [MeshPacket.Delayed] = [ .noDelay, .broadcast, .direct, @@ -2022,7 +2037,7 @@ struct LogRecord { extension LogRecord.Level: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [LogRecord.Level] = [ + static var allCases: [LogRecord.Level] = [ .unset, .critical, .error, @@ -2762,6 +2777,8 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding { 58: .same(proto: "HELTEC_WIRELESS_TRACKER_V1_0"), 59: .same(proto: "UNPHONE"), 60: .same(proto: "TD_LORAC"), + 61: .same(proto: "CDEBYTE_EORA_S3"), + 62: .same(proto: "TWC_MESH_V4"), 255: .same(proto: "PRIVATE_HW"), ] } @@ -2843,15 +2860,7 @@ extension Position: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB var _seqNumber: UInt32 = 0 var _precisionBits: UInt32 = 0 - #if swift(>=5.10) - // This property is used as the initial default value for new instances of the type. - // The type itself is protecting the reference to its storage via CoW semantics. - // This will force a copy to be made of this reference when the first mutation occurs; - // hence, it is safe to mark this as `nonisolated(unsafe)`. - static nonisolated(unsafe) let defaultInstance = _StorageClass() - #else - static let defaultInstance = _StorageClass() - #endif + static let defaultInstance = _StorageClass() private init() {} @@ -3513,15 +3522,7 @@ extension MeshPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio var _viaMqtt: Bool = false var _hopStart: 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 + static let defaultInstance = _StorageClass() private init() {} @@ -3733,15 +3734,7 @@ extension NodeInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB var _hopsAway: UInt32 = 0 var _isFavorite: Bool = false - #if swift(>=5.10) - // This property is used as the initial default value for new instances of the type. - // The type itself is protecting the reference to its storage via CoW semantics. - // This will force a copy to be made of this reference when the first mutation occurs; - // hence, it is safe to mark this as `nonisolated(unsafe)`. - static nonisolated(unsafe) let defaultInstance = _StorageClass() - #else - static let defaultInstance = _StorageClass() - #endif + static let defaultInstance = _StorageClass() private init() {} @@ -4033,15 +4026,7 @@ extension FromRadio: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation var _id: UInt32 = 0 var _payloadVariant: FromRadio.OneOf_PayloadVariant? - #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 + static let defaultInstance = _StorageClass() private init() {} diff --git a/Meshtastic/Protobufs/meshtastic/module_config.pb.swift b/Meshtastic/Protobufs/meshtastic/module_config.pb.swift index f6c28745..b688479b 100644 --- a/Meshtastic/Protobufs/meshtastic/module_config.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/module_config.pb.swift @@ -64,7 +64,7 @@ enum RemoteHardwarePinType: SwiftProtobuf.Enum { extension RemoteHardwarePinType: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [RemoteHardwarePinType] = [ + static var allCases: [RemoteHardwarePinType] = [ .unknown, .digitalRead, .digitalWrite, @@ -1152,7 +1152,7 @@ struct ModuleConfig { extension ModuleConfig.AudioConfig.Audio_Baud: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [ModuleConfig.AudioConfig.Audio_Baud] = [ + static var allCases: [ModuleConfig.AudioConfig.Audio_Baud] = [ .codec2Default, .codec23200, .codec22400, @@ -1167,7 +1167,7 @@ extension ModuleConfig.AudioConfig.Audio_Baud: CaseIterable { extension ModuleConfig.SerialConfig.Serial_Baud: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [ModuleConfig.SerialConfig.Serial_Baud] = [ + static var allCases: [ModuleConfig.SerialConfig.Serial_Baud] = [ .baudDefault, .baud110, .baud300, @@ -1189,7 +1189,7 @@ extension ModuleConfig.SerialConfig.Serial_Baud: CaseIterable { extension ModuleConfig.SerialConfig.Serial_Mode: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [ModuleConfig.SerialConfig.Serial_Mode] = [ + static var allCases: [ModuleConfig.SerialConfig.Serial_Mode] = [ .default, .simple, .proto, @@ -1201,7 +1201,7 @@ extension ModuleConfig.SerialConfig.Serial_Mode: CaseIterable { extension ModuleConfig.CannedMessageConfig.InputEventChar: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [ModuleConfig.CannedMessageConfig.InputEventChar] = [ + static var allCases: [ModuleConfig.CannedMessageConfig.InputEventChar] = [ .none, .up, .down, diff --git a/Meshtastic/Protobufs/meshtastic/portnums.pb.swift b/Meshtastic/Protobufs/meshtastic/portnums.pb.swift index a67e5ae6..c8948d7d 100644 --- a/Meshtastic/Protobufs/meshtastic/portnums.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/portnums.pb.swift @@ -277,7 +277,7 @@ enum PortNum: SwiftProtobuf.Enum { extension PortNum: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [PortNum] = [ + static var allCases: [PortNum] = [ .unknownApp, .textMessageApp, .remoteHardwareApp, diff --git a/Meshtastic/Protobufs/meshtastic/remote_hardware.pb.swift b/Meshtastic/Protobufs/meshtastic/remote_hardware.pb.swift index b26a80a2..1f24d0db 100644 --- a/Meshtastic/Protobufs/meshtastic/remote_hardware.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/remote_hardware.pb.swift @@ -119,7 +119,7 @@ struct HardwareMessage { extension HardwareMessage.TypeEnum: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [HardwareMessage.TypeEnum] = [ + static var allCases: [HardwareMessage.TypeEnum] = [ .unset, .writeGpios, .watchGpios, diff --git a/Meshtastic/Protobufs/meshtastic/storeforward.pb.swift b/Meshtastic/Protobufs/meshtastic/storeforward.pb.swift index 697b4e87..a8fa5f90 100644 --- a/Meshtastic/Protobufs/meshtastic/storeforward.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/storeforward.pb.swift @@ -344,7 +344,7 @@ struct StoreAndForward { extension StoreAndForward.RequestResponse: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [StoreAndForward.RequestResponse] = [ + static var allCases: [StoreAndForward.RequestResponse] = [ .unset, .routerError, .routerHeartbeat, diff --git a/Meshtastic/Protobufs/meshtastic/telemetry.pb.swift b/Meshtastic/Protobufs/meshtastic/telemetry.pb.swift index d878629a..5f96c02b 100644 --- a/Meshtastic/Protobufs/meshtastic/telemetry.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/telemetry.pb.swift @@ -88,6 +88,10 @@ enum TelemetrySensorType: SwiftProtobuf.Enum { /// /// BMP085/BMP180 High accuracy temperature and pressure (older Version of BMP280) case bmp085 // = 15 + + /// + /// RCWL-9620 Doppler Radar Distance Sensor, used for water level detection + case rcwl9620 // = 16 case UNRECOGNIZED(Int) init() { @@ -112,6 +116,7 @@ enum TelemetrySensorType: SwiftProtobuf.Enum { case 13: self = .pmsa003I case 14: self = .ina3221 case 15: self = .bmp085 + case 16: self = .rcwl9620 default: self = .UNRECOGNIZED(rawValue) } } @@ -134,6 +139,7 @@ enum TelemetrySensorType: SwiftProtobuf.Enum { case .pmsa003I: return 13 case .ina3221: return 14 case .bmp085: return 15 + case .rcwl9620: return 16 case .UNRECOGNIZED(let i): return i } } @@ -144,7 +150,7 @@ enum TelemetrySensorType: SwiftProtobuf.Enum { extension TelemetrySensorType: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [TelemetrySensorType] = [ + static var allCases: [TelemetrySensorType] = [ .sensorUnset, .bme280, .bme680, @@ -161,6 +167,7 @@ extension TelemetrySensorType: CaseIterable { .pmsa003I, .ina3221, .bmp085, + .rcwl9620, ] } @@ -234,6 +241,10 @@ struct EnvironmentMetrics { /// Belongs to Air Quality but is not particle but VOC measurement. Other VOC values can also be put in here. var iaq: UInt32 = 0 + /// + /// RCWL9620 Doppler Radar Distance Sensor, used for water level detection. Float value in mm. + var distance: Float = 0 + var unknownFields = SwiftProtobuf.UnknownStorage() init() {} @@ -467,6 +478,7 @@ extension TelemetrySensorType: SwiftProtobuf._ProtoNameProviding { 13: .same(proto: "PMSA003I"), 14: .same(proto: "INA3221"), 15: .same(proto: "BMP085"), + 16: .same(proto: "RCWL9620"), ] } @@ -536,6 +548,7 @@ extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImple 5: .same(proto: "voltage"), 6: .same(proto: "current"), 7: .same(proto: "iaq"), + 8: .same(proto: "distance"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -551,6 +564,7 @@ extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImple case 5: try { try decoder.decodeSingularFloatField(value: &self.voltage) }() case 6: try { try decoder.decodeSingularFloatField(value: &self.current) }() case 7: try { try decoder.decodeSingularUInt32Field(value: &self.iaq) }() + case 8: try { try decoder.decodeSingularFloatField(value: &self.distance) }() default: break } } @@ -578,6 +592,9 @@ extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImple if self.iaq != 0 { try visitor.visitSingularUInt32Field(value: self.iaq, fieldNumber: 7) } + if self.distance != 0 { + try visitor.visitSingularFloatField(value: self.distance, fieldNumber: 8) + } try unknownFields.traverse(visitor: &visitor) } @@ -589,6 +606,7 @@ extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImple if lhs.voltage != rhs.voltage {return false} if lhs.current != rhs.current {return false} if lhs.iaq != rhs.iaq {return false} + if lhs.distance != rhs.distance {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/Meshtastic/Protobufs/meshtastic/xmodem.pb.swift b/Meshtastic/Protobufs/meshtastic/xmodem.pb.swift index 4df972b8..a70841f2 100644 --- a/Meshtastic/Protobufs/meshtastic/xmodem.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/xmodem.pb.swift @@ -88,7 +88,7 @@ struct XModem { extension XModem.Control: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. - static let allCases: [XModem.Control] = [ + static var allCases: [XModem.Control] = [ .nul, .soh, .stx, diff --git a/protobufs b/protobufs index f92900c5..86640f20 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit f92900c5f884b04388fb7abf61d4df66783015e4 +Subproject commit 86640f20db7b9b5be42949d18e8d96ad10d47a68 From 50000e181e6f25aa38315faf33b9318621870ae8 Mon Sep 17 00:00:00 2001 From: shengminchen Date: Wed, 1 May 2024 01:39:47 +0200 Subject: [PATCH 7/7] completed translations for zh-Hans (except those for logs) --- zh-Hans.lproj/Localizable.strings | 106 +++++++++++++++--------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/zh-Hans.lproj/Localizable.strings b/zh-Hans.lproj/Localizable.strings index 93189cc8..d0371670 100644 --- a/zh-Hans.lproj/Localizable.strings +++ b/zh-Hans.lproj/Localizable.strings @@ -7,19 +7,19 @@ */ "about"="关于"; "about.meshtastic"="关于 Meshtastic"; -"activity"="Activity"; +"activity"="活动"; "admin"="管理员"; "admin.log"="管理员消息日志"; -"ago"="ago"; +"ago"="之前"; "airtime"="广播时间"; "always.on"="常亮"; -"ambient.lighting"="Ambient Lighting"; -"ambient.lighting.config"="Ambient Lighting Config"; +"ambient.lighting"="氛围灯"; +"ambient.lighting.config"="氛围灯配置"; "appsettings"="通用设置"; "appsettings.provide.location"="提供定位到 Mesh 网络"; -"appsettings.smartposition"="Smart Position"; +"appsettings.smartposition"="智能定位"; "are.you.sure"="是否确认?"; -"ascii.capable"="ASCII Capable"; +"ascii.capable"="ASCII 兼容"; "available.radios"="可以连接的电台"; "automatic.detection"="自动识别"; "battery.level"="电池电量"; @@ -52,28 +52,28 @@ "clear.app.data"="清除 App 数据"; "clear.log"="清除日志"; "close"="关闭"; -"config.power.settings"="Power"; -"config.power.title"="Power Config"; -"config.power.section.battery"="Battery"; -"config.power.section.sleep"="Sleep"; -"config.power.adc.override"="ADC Override"; -"config.power.adc.multiplier"="Multiplier"; -"config.power.ls.secs"="Light Sleep Interval"; -"config.power.min.wake.secs"="Minimum Wake Interval"; -"config.power.saving"="Power Saving"; -"config.power.saving.description"="Will sleep everything as much as possible, for the tracker and sensor role this will also include the lora radio. Don't use this setting if you want to use your device with the phone apps or are using a device without a user button."; -"config.power.shutdown.on.power.loss"="Shutdown on Power Loss"; +"config.power.settings"="电源"; +"config.power.title"="电源配置"; +"config.power.section.battery"="电池"; +"config.power.section.sleep"="休眠"; +"config.power.adc.override"="ADC 修改"; +"config.power.adc.multiplier"="ADC 放大"; +"config.power.ls.secs"="轻度睡眠间隔"; +"config.power.min.wake.secs"="最小唤醒间隔"; +"config.power.saving"="省电模式"; +"config.power.saving.description"="尽可能将所有部件都置于休眠状态,对于跟踪器和传感器功能,这还包括 LoRa 无线电。如果您要使用手机应用程序或者使用没有用户按钮的设备,请不要使用这个设置。"; +"config.power.shutdown.on.power.loss"="断电时关机"; "config.power.shutdown.after.secs"="After"; -"config.power.wait.bluetooth.secs"="Bluetooth Off After"; -"config.ringtone"="RTTTL Ringtone"; -"config.ringtone.title"="Ringtone Config"; -"config.ringtone.label"="Ringtone Transfer Language"; -"config.ringtone.description"="Ringtone Transfer Language(RTTTL) Ringtone String used by supported buzzers in external notifications."; -"config.module.paxcounter.settings"="PAX Counter"; -"config.module.paxcounter.title"="PAX Counter Config"; -"config.module.paxcounter.enabled.description"="When enabled the PAX Counter module counts the number of people passing by using WiFi and Bluetooth. Both WiFI and Bluetooth must be disabled for PAX counter to work."; -"config.module.paxcounter.updateinterval"="Update Interval"; -"config.module.paxcounter.updateinterval.description"="How often we can send a message to the mesh when people are detected."; +"config.power.wait.bluetooth.secs"="蓝牙关闭 After"; +"config.ringtone"="RTTTL 铃声"; +"config.ringtone.title"="铃声配置"; +"config.ringtone.label"="铃声传输语言"; +"config.ringtone.description"="支持外部通知中使用的铃声传输语言 (RTTTL) 铃声字符串。"; +"config.module.paxcounter.settings"="PAX 计数器"; +"config.module.paxcounter.title"="PAX 计数器配置"; +"config.module.paxcounter.enabled.description"="启用 PAX 计数器模块时,通过使用 WiFi 和蓝牙来计算经过的人数。为了使 PAX 计数器正常工作,必须将 WiFi 和蓝牙都禁用。"; +"config.module.paxcounter.updateinterval"="更新间隔"; +"config.module.paxcounter.updateinterval.description"="检测到人员时,我们可以隔多久发送一条消息到 Mesh"; "config.save.confirm"="电台将会在配置保存后重启。"; "connected.radio"="已连接的电台"; "communicating"="与电台进行通讯中..."; @@ -85,24 +85,24 @@ "current"="当前"; "default"="默认"; "delete"="删除"; -"detection.sensor"="Detection Sensor"; +"detection.sensor"="检测传感器"; "device"="电台"; "device.config"="电台配置"; -"device.configuration"="Device Configuration"; +"device.configuration"="设备配置"; "device.metrics.delete"="删除所有电台指标?"; "device.metrics.log"="电台指标日志"; "device.role.client"="标准模式 - App 可以连接到电台进行收发操作,并且会自动转发 Mesh 网络中其他节点的消息。"; "device.role.clientmute"="静默模式 - 与标准模式类似,App 可以连接到电台进行收发操作,但不会转发 Mesh 网络中其他节点的消息。"; -"device.role.clienthidden"=" Used for nodes that \"only speak when spoken to\" Turns all of the routine broadcasts but allows for ad-hoc communication. Still rebroadcasts, but with local only rebroadcast mode (known meshes only). Can be used for private operation or to dramatically reduce airtime / power consumption."; -"device.role.lostandfound"="Used to automatically send a text message to the mesh with the current position of the device on a frequent interval: \"I'm lost! Position: lat / long\""; +"device.role.clienthidden"="用于\"只有在被请求时才发言\"的节点。关闭所有常规广播,但允许临时通信。仍然会进行转发,但采用本地转发模式(仅限已知的网络)。可用于私密操作或大幅减少空中时间/功耗。"; +"device.role.lostandfound"="用于定期自动向 Mesh 发送包含设备当前位置的文本消息的功能:\"I'm lost! Position: lat / long\""; "device.role.router"="纯路由模式 - 自动转发 Mesh 网络中其他节点的消息,中继模式下屏幕会熄灭,Wi-Fi 和蓝牙将会进入睡眠模式,App 将无法连接到电台进行收发操作。"; "device.role.routerclient"="路由客户端模式 - 优先转发 Mesh 网络中其他节点的消息,App 也可以连接到电台进行收发操作。"; -"device.role.repeater"="中继模式 - Mesh 网络数据包将优先通过此节点路由。此模式可消除不必要的开销,如 NodeInfo、DeviceTelemetry 和任何其他 Mesh 数据包,从而使设备不显示为 Mesh 网络的一部分。有关此角色的其他特定设置,请参阅转播模式。"; +"device.role.repeater"="中继模式 - Mesh 网络数据包将优先通过此节点路由。此模式可消除不必要的开销,如节点信息、设备遥测和任何其他 Mesh 数据包,从而使设备不显示为 Mesh 网络的一部分。有关此角色的其他特定设置,请参阅转播模式。"; "device.role.tracker"="定位模式 - 用于作为 GPS 跟踪器。从该设备发送的定位数据包优先级较高,每两分钟广播一次。智能位置广播默认为关闭。"; -"device.role.lostandfound"="Broadcasts location as message to default channel regularly for to assist with device recovery."; -"device.role.sensor"="Broadcasts telemetry packets as priority."; -"device.role.tak"="Optimized for ATAK system communication, reduces routine broadcasts."; -"device.role.taktracker"="Enables automatic TAK PLI broadcasts and reduces routine broadcasts."; +"device.role.lostandfound"="定期向默认通道广播位置信息,以帮助寻回设备。"; +"device.role.sensor"="将遥测数据包优先广播。"; +"device.role.tak"="针对 ATAK 系统通信进行优化,减少常规广播。"; +"device.role.taktracker"="启用自动 TAK PLI(Position Location Information)广播,并减少常规广播。"; "direct.messages"="直频消息"; "dismiss.keyboard"="隐藏键盘"; "display"="屏幕(电台屏幕)"; @@ -119,8 +119,8 @@ "finish"="Finish"; "firmware.version"="固件版本"; "firmware.version.unsupported"="检测到不支持的固件版本,无法连接到电台。"; -"gas"="Gas"; -"gas.resistance"="Gas Resistance"; +"gas"="气体"; +"gas.resistance"="气体阻抗"; "generate.qr.code"="生成二维码"; "gpsformat.dec"="十进制"; "gpsformat.dms"="度分秒"; @@ -178,12 +178,12 @@ "map.centering"="居中"; "map.tiles.delete"="删除已缓存的地图区块"; "map.recentering"="自动重新居中"; -"map.use.legacy"="Use Legacy Mesh Map"; +"map.use.legacy"="使用传统网状地图"; "map.type"="地图类型"; "map.usertrackingmode"="用户跟随模式"; "map.usertrackingmode.none"="无"; "map.usertrackingmode.follow"="跟随"; -"map.usertrackingmode.followwithheading"="Follow with heading"; +"map.usertrackingmode.followwithheading"="跟随航向"; "mesh.live.activity"="Mesh 实时活动"; "mesh.log"="Mesh 日志"; "mesh.log.ambientlighting.config %@"="Ambient Lighting module config received: %@"; @@ -231,17 +231,17 @@ "mode"="模式"; "module.configuration"="模块配置"; "mqtt"="MQTT"; -"mqtt.connect"="Connect to MQTT"; +"mqtt.connect"="连接至 MQTT"; "mqtt.config"="MQTT 配置"; "mqtt.clientproxy"="MQTT 客户端代理"; -"mqtt.disconnect"="Disconnect from MQTT"; +"mqtt.disconnect"="断开 MQTT 连接"; "mqtt.username"="用户名称"; "name"="名称"; "network"="网络"; "network.config"="网络配置"; "nodes"="节点"; "nodes %@"="节点 (%@)"; -"nodelist.filter.distance %@"="up to %@ away"; +"nodelist.filter.distance %@"="最远距离 %@"; "no.nodes"="未找到 Meshtastic 节点"; "not.connected"="未连接到电台"; "numbers.punctuation"="数字和标点符号"; @@ -250,7 +250,7 @@ "on.boot"="仅在启动时"; "options"="选项"; "password"="密码"; -"pause"="Pause"; +"pause"="暂停"; "phone.gps"="手机 GPS"; "phone.gps.interval.description"="电台通过手机获取定位的时间间隔,但是向 Mesh 网络中刷新定位的时间间隔由电台控制。"; "position"="定位"; @@ -265,11 +265,11 @@ "reboot.node"="重启节点?"; "received.ack"="收到确认"; "received.ack.real"="收件人确认"; -"resume"="Resume"; +"resume"="恢复"; "ringtone"="铃声"; "ringtone.config"="铃声设置"; -"route.recorder"="Route Recorder"; -"routes"="Routes"; +"route.recorder"="路径记录器"; +"routes"="路径"; "routing.acknowledged"="确认"; "routing.noroute"="找不到目标"; "routing.gotnak"="收到否认"; @@ -303,7 +303,7 @@ "set.region"="设置 LoRa 区域"; "standard"="标准"; "standard.muted"="标准静音"; -"start"="Start"; +"start"="开始"; "ssid"="SSID"; "storeforward"="储存 & 转发"; "storeforward.config"="储存 & 转发设置"; @@ -323,10 +323,10 @@ "timestamp"="时间戳"; "tip.bluetooth.connect.title"="连接到 LoRa 电台"; "tip.bluetooth.connect.message"="显示当前通过蓝牙连接的 Lora 电台的信息。您可以向左滑动断开电台,长按查看统计信息或开始实时活动。"; -"tip.channel.admin.title"="Admin Channel"; -"tip.channel.admin.message"="Admin channel detected: Select a node from the drop down to manage connected or remote devices."; -"tip.channels.create.title"="Manage Channels"; -"tip.channels.create.message"="Most data on your mesh is sent over the primary channel. You can set up secondary channels to create additional messaging groups secured by their own key. [Channel config tips](https://meshtastic.org/docs/configuration/radio/channels/)"; +"tip.channel.admin.title"="admin 频道"; +"tip.channel.admin.message"="检测到 admin 频道:请从下拉菜单中选择一个节点,来管理已连接或远程设备。"; +"tip.channels.create.title"="管理频道"; +"tip.channels.create.message"="大多数 Mesh 上的数据都通过主要频道发送。您可以设置次要频道,以创建额外的消息组,并通过其自己的密钥进行安全保护。 [频道配置提示](https://meshtastic.org/docs/configuration/radio/channels/)"; "tip.channels.share.title"="共享 Meshtastic 频道"; "tip.channels.share.message"="在 Meshtastic 网络中最多有 8 个频道。第一个频道是主频道,大多数活动都发生在这里,也是必需的。如果您不共享主频道,您的第一个共享频道就会成为其他网络的主频道。它会在其主频道和您的辅助频道上对话。名称为 admin 的频道可远程控制节点。其他频道用于私人群组,每个群组都有自己的密钥。"; "tip.messages.title"="消息"; @@ -341,4 +341,4 @@ "user.details"="用户信息"; "voltage"="电压"; "waiting"="等待中..."; -"appsettings.newNodeNotifications"="New Node Notifications"; +"appsettings.newNodeNotifications"="新节点通知";