Merge pull request #361 from meshtastic/2.1.11_Working_Changes
2.1.11 working changes
3
.gitignore
vendored
|
|
@ -5,6 +5,9 @@
|
|||
## User settings
|
||||
xcuserdata/
|
||||
|
||||
## Generated Protobufs
|
||||
protobufs/
|
||||
|
||||
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
|
||||
*.xcscmblueprint
|
||||
*.xccheckout
|
||||
|
|
|
|||
|
|
@ -104,6 +104,8 @@
|
|||
DDB75A162A0594AD006ED576 /* TileOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB75A152A0594AD006ED576 /* TileOverlay.swift */; };
|
||||
DDB75A1A2A05EB67006ED576 /* alpha.png in Resources */ = {isa = PBXBuildFile; fileRef = DDB75A192A05EB67006ED576 /* alpha.png */; };
|
||||
DDB75A1E2A0B0CD0006ED576 /* LoRaSignalStrengthIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB75A1D2A0B0CD0006ED576 /* LoRaSignalStrengthIndicator.swift */; };
|
||||
DDB75A212A12B954006ED576 /* LoRaSignalStrength.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB75A202A12B954006ED576 /* LoRaSignalStrength.swift */; };
|
||||
DDB75A232A13CDA9006ED576 /* BatteryLevelCompact.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB75A222A13CDA9006ED576 /* BatteryLevelCompact.swift */; };
|
||||
DDC2E15826CE248E0042C5E4 /* MeshtasticApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E15726CE248E0042C5E4 /* MeshtasticApp.swift */; };
|
||||
DDC2E15C26CE248F0042C5E4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */; };
|
||||
DDC2E15F26CE248F0042C5E4 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E15E26CE248F0042C5E4 /* Preview Assets.xcassets */; };
|
||||
|
|
@ -294,6 +296,8 @@
|
|||
DDB75A192A05EB67006ED576 /* alpha.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = alpha.png; sourceTree = "<group>"; };
|
||||
DDB75A1D2A0B0CD0006ED576 /* LoRaSignalStrengthIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoRaSignalStrengthIndicator.swift; sourceTree = "<group>"; };
|
||||
DDB75A1F2A10766D006ED576 /* MeshtasticDataModelV13.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV13.xcdatamodel; sourceTree = "<group>"; };
|
||||
DDB75A202A12B954006ED576 /* LoRaSignalStrength.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoRaSignalStrength.swift; sourceTree = "<group>"; };
|
||||
DDB75A222A13CDA9006ED576 /* BatteryLevelCompact.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryLevelCompact.swift; sourceTree = "<group>"; };
|
||||
DDBA45EC299ED78100DEEDDC /* MeshtasticDataModelV8.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV8.xcdatamodel; sourceTree = "<group>"; };
|
||||
DDC2E15426CE248E0042C5E4 /* Meshtastic.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Meshtastic.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DDC2E15726CE248E0042C5E4 /* MeshtasticApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticApp.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -694,6 +698,8 @@
|
|||
DD97E96528EFD9820056DDA4 /* MeshtasticLogo.swift */,
|
||||
DD457187293C7E63000C49FB /* BLESignalStrengthIndicator.swift */,
|
||||
DDB75A1D2A0B0CD0006ED576 /* LoRaSignalStrengthIndicator.swift */,
|
||||
DDB75A202A12B954006ED576 /* LoRaSignalStrength.swift */,
|
||||
DDB75A222A13CDA9006ED576 /* BatteryLevelCompact.swift */,
|
||||
);
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1008,6 +1014,7 @@
|
|||
DD2160AF28C5552500C17253 /* MQTTConfig.swift in Sources */,
|
||||
DDDB444229F8A88700EE2349 /* Double.swift in Sources */,
|
||||
DD5E520F298EE33B00D21B61 /* cannedmessages.pb.swift in Sources */,
|
||||
DDB75A232A13CDA9006ED576 /* BatteryLevelCompact.swift in Sources */,
|
||||
DDB75A162A0594AD006ED576 /* TileOverlay.swift in Sources */,
|
||||
DDF924CA26FBB953009FE055 /* ConnectedDevice.swift in Sources */,
|
||||
DD3CC6BE28E4CD9800FA9159 /* BatteryGauge.swift in Sources */,
|
||||
|
|
@ -1060,6 +1067,7 @@
|
|||
DDB75A1E2A0B0CD0006ED576 /* LoRaSignalStrengthIndicator.swift in Sources */,
|
||||
DDA6B2E928419CF2003E8C16 /* MeshPackets.swift in Sources */,
|
||||
DDCE4E2C2869F92900BE9F8F /* UserConfig.swift in Sources */,
|
||||
DDB75A212A12B954006ED576 /* LoRaSignalStrength.swift in Sources */,
|
||||
DD6193752862F6E600E59241 /* ExternalNotificationConfig.swift in Sources */,
|
||||
DDB6ABE428B13FFF00384BA1 /* DisplayEnums.swift in Sources */,
|
||||
DD86D40A287F04F100BAEB7A /* InvalidVersion.swift in Sources */,
|
||||
|
|
@ -1298,7 +1306,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.1.10;
|
||||
MARKETING_VERSION = 2.1.11;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -1332,7 +1340,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.1.10;
|
||||
MARKETING_VERSION = 2.1.11;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -1451,7 +1459,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.1.10;
|
||||
MARKETING_VERSION = 2.1.11;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
|
|
@ -1482,7 +1490,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.1.10;
|
||||
MARKETING_VERSION = 2.1.11;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "play_store_icon_114px-2.png",
|
||||
"filename" : "heltecwsl 2.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "play_store_icon_114px-3.png",
|
||||
"filename" : "heltecwsl 1.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "play_store_icon_114px-4.png",
|
||||
"filename" : "heltecwsl.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
|
|
|
|||
BIN
Meshtastic/Assets.xcassets/HELTECWSLV3.imageset/heltecwsl 1.png
vendored
Normal file
|
After Width: | Height: | Size: 130 KiB |
BIN
Meshtastic/Assets.xcassets/HELTECWSLV3.imageset/heltecwsl 2.png
vendored
Normal file
|
After Width: | Height: | Size: 130 KiB |
BIN
Meshtastic/Assets.xcassets/HELTECWSLV3.imageset/heltecwsl.png
vendored
Normal file
|
After Width: | Height: | Size: 130 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
|
@ -1,17 +1,17 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "play_store_icon_114px-2.png",
|
||||
"filename" : "tbeam_supreme 2.jpg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "play_store_icon_114px-3.png",
|
||||
"filename" : "tbeam_supreme 1.jpg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "play_store_icon_114px-4.png",
|
||||
"filename" : "tbeam_supreme.jpg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 12 KiB |
BIN
Meshtastic/Assets.xcassets/LILYGOTBEAMS3CORE.imageset/tbeam_supreme 1.jpg
vendored
Normal file
|
After Width: | Height: | Size: 533 KiB |
BIN
Meshtastic/Assets.xcassets/LILYGOTBEAMS3CORE.imageset/tbeam_supreme 2.jpg
vendored
Normal file
|
After Width: | Height: | Size: 533 KiB |
BIN
Meshtastic/Assets.xcassets/LILYGOTBEAMS3CORE.imageset/tbeam_supreme.jpg
vendored
Normal file
|
After Width: | Height: | Size: 533 KiB |
|
|
@ -269,6 +269,12 @@ enum MapTileServer: String, CaseIterable, Identifiable {
|
|||
}
|
||||
}
|
||||
|
||||
enum OverlayType: String, CaseIterable, Equatable {
|
||||
case tileServer
|
||||
case geoJson
|
||||
var localized: String { self.rawValue.localized }
|
||||
}
|
||||
|
||||
enum MapOverlayServer: String, CaseIterable, Identifiable {
|
||||
|
||||
case baseReReflectivityCurrent
|
||||
|
|
@ -282,6 +288,29 @@ enum MapOverlayServer: String, CaseIterable, Identifiable {
|
|||
case mrmsHybridScanReflectivityComposite
|
||||
|
||||
var id: String { self.rawValue }
|
||||
var overlayType: OverlayType {
|
||||
switch self {
|
||||
|
||||
case .baseReReflectivityCurrent:
|
||||
return .tileServer
|
||||
case .baseReReflectivityOneHourAgo:
|
||||
return .tileServer
|
||||
case .echoTopsEetCurrent:
|
||||
return .tileServer
|
||||
case .echoTopsEetOneHourAgo:
|
||||
return .tileServer
|
||||
case .q2OneHourPrecipitation:
|
||||
return .tileServer
|
||||
case .q2TwentyFourHourPrecipitation:
|
||||
return .tileServer
|
||||
case .q2FortyEightHourPrecipitation:
|
||||
return .tileServer
|
||||
case .q2SeventyTwoHourPrecipitation:
|
||||
return .tileServer
|
||||
case .mrmsHybridScanReflectivityComposite:
|
||||
return .tileServer
|
||||
}
|
||||
}
|
||||
var attribution: String {
|
||||
return "NEXRAD Weather tiles from Iowa State University Environmental Mesonet [OGC Web Services](https://mesonet.agron.iastate.edu/ogc/)."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -204,6 +204,26 @@ struct AdminMessage {
|
|||
set {payloadVariant = .setHamMode(newValue)}
|
||||
}
|
||||
|
||||
///
|
||||
/// Get the mesh's nodes with their available gpio pins for RemoteHardware module use
|
||||
var getNodeRemoteHardwarePinsRequest: Bool {
|
||||
get {
|
||||
if case .getNodeRemoteHardwarePinsRequest(let v)? = payloadVariant {return v}
|
||||
return false
|
||||
}
|
||||
set {payloadVariant = .getNodeRemoteHardwarePinsRequest(newValue)}
|
||||
}
|
||||
|
||||
///
|
||||
/// Respond with the mesh's nodes with their available gpio pins for RemoteHardware module use
|
||||
var getNodeRemoteHardwarePinsResponse: NodeRemoteHardwarePinsResponse {
|
||||
get {
|
||||
if case .getNodeRemoteHardwarePinsResponse(let v)? = payloadVariant {return v}
|
||||
return NodeRemoteHardwarePinsResponse()
|
||||
}
|
||||
set {payloadVariant = .getNodeRemoteHardwarePinsResponse(newValue)}
|
||||
}
|
||||
|
||||
///
|
||||
/// Set the owner for this node
|
||||
var setOwner: User {
|
||||
|
|
@ -409,6 +429,12 @@ struct AdminMessage {
|
|||
/// Setup a node for licensed amateur (ham) radio operation
|
||||
case setHamMode(HamParameters)
|
||||
///
|
||||
/// Get the mesh's nodes with their available gpio pins for RemoteHardware module use
|
||||
case getNodeRemoteHardwarePinsRequest(Bool)
|
||||
///
|
||||
/// Respond with the mesh's nodes with their available gpio pins for RemoteHardware module use
|
||||
case getNodeRemoteHardwarePinsResponse(NodeRemoteHardwarePinsResponse)
|
||||
///
|
||||
/// Set the owner for this node
|
||||
case setOwner(User)
|
||||
///
|
||||
|
|
@ -532,6 +558,14 @@ struct AdminMessage {
|
|||
guard case .setHamMode(let l) = lhs, case .setHamMode(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.getNodeRemoteHardwarePinsRequest, .getNodeRemoteHardwarePinsRequest): return {
|
||||
guard case .getNodeRemoteHardwarePinsRequest(let l) = lhs, case .getNodeRemoteHardwarePinsRequest(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.getNodeRemoteHardwarePinsResponse, .getNodeRemoteHardwarePinsResponse): return {
|
||||
guard case .getNodeRemoteHardwarePinsResponse(let l) = lhs, case .getNodeRemoteHardwarePinsResponse(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.setOwner, .setOwner): return {
|
||||
guard case .setOwner(let l) = lhs, case .setOwner(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
|
|
@ -803,12 +837,29 @@ struct HamParameters {
|
|||
init() {}
|
||||
}
|
||||
|
||||
///
|
||||
/// Response envelope for node_remote_hardware_pins
|
||||
struct NodeRemoteHardwarePinsResponse {
|
||||
// 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.
|
||||
|
||||
///
|
||||
/// Nodes and their respective remote hardware GPIO pins
|
||||
var nodeRemoteHardwarePins: [NodeRemoteHardwarePin] = []
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension AdminMessage: @unchecked Sendable {}
|
||||
extension AdminMessage.OneOf_PayloadVariant: @unchecked Sendable {}
|
||||
extension AdminMessage.ConfigType: @unchecked Sendable {}
|
||||
extension AdminMessage.ModuleConfigType: @unchecked Sendable {}
|
||||
extension HamParameters: @unchecked Sendable {}
|
||||
extension NodeRemoteHardwarePinsResponse: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
|
@ -835,6 +886,8 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
|
|||
16: .standard(proto: "get_device_connection_status_request"),
|
||||
17: .standard(proto: "get_device_connection_status_response"),
|
||||
18: .standard(proto: "set_ham_mode"),
|
||||
19: .standard(proto: "get_node_remote_hardware_pins_request"),
|
||||
20: .standard(proto: "get_node_remote_hardware_pins_response"),
|
||||
32: .standard(proto: "set_owner"),
|
||||
33: .standard(proto: "set_channel"),
|
||||
34: .standard(proto: "set_config"),
|
||||
|
|
@ -1028,6 +1081,27 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
|
|||
self.payloadVariant = .setHamMode(v)
|
||||
}
|
||||
}()
|
||||
case 19: try {
|
||||
var v: Bool?
|
||||
try decoder.decodeSingularBoolField(value: &v)
|
||||
if let v = v {
|
||||
if self.payloadVariant != nil {try decoder.handleConflictingOneOf()}
|
||||
self.payloadVariant = .getNodeRemoteHardwarePinsRequest(v)
|
||||
}
|
||||
}()
|
||||
case 20: try {
|
||||
var v: NodeRemoteHardwarePinsResponse?
|
||||
var hadOneofValue = false
|
||||
if let current = self.payloadVariant {
|
||||
hadOneofValue = true
|
||||
if case .getNodeRemoteHardwarePinsResponse(let m) = current {v = m}
|
||||
}
|
||||
try decoder.decodeSingularMessageField(value: &v)
|
||||
if let v = v {
|
||||
if hadOneofValue {try decoder.handleConflictingOneOf()}
|
||||
self.payloadVariant = .getNodeRemoteHardwarePinsResponse(v)
|
||||
}
|
||||
}()
|
||||
case 32: try {
|
||||
var v: User?
|
||||
var hadOneofValue = false
|
||||
|
|
@ -1239,6 +1313,14 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
|
|||
guard case .setHamMode(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 18)
|
||||
}()
|
||||
case .getNodeRemoteHardwarePinsRequest?: try {
|
||||
guard case .getNodeRemoteHardwarePinsRequest(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularBoolField(value: v, fieldNumber: 19)
|
||||
}()
|
||||
case .getNodeRemoteHardwarePinsResponse?: try {
|
||||
guard case .getNodeRemoteHardwarePinsResponse(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 20)
|
||||
}()
|
||||
case .setOwner?: try {
|
||||
guard case .setOwner(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 32)
|
||||
|
|
@ -1382,3 +1464,35 @@ extension HamParameters: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa
|
|||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension NodeRemoteHardwarePinsResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".NodeRemoteHardwarePinsResponse"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .standard(proto: "node_remote_hardware_pins"),
|
||||
]
|
||||
|
||||
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.decodeRepeatedMessageField(value: &self.nodeRemoteHardwarePins) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
if !self.nodeRemoteHardwarePins.isEmpty {
|
||||
try visitor.visitRepeatedMessageField(value: self.nodeRemoteHardwarePins, fieldNumber: 1)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: NodeRemoteHardwarePinsResponse, rhs: NodeRemoteHardwarePinsResponse) -> Bool {
|
||||
if lhs.nodeRemoteHardwarePins != rhs.nodeRemoteHardwarePins {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -204,7 +204,7 @@ struct Config {
|
|||
|
||||
///
|
||||
/// Router device role.
|
||||
/// Mesh packets will prefer to be routed over this node. This node will not be used by client apps.
|
||||
/// Mesh packets will prefer to be routed over this node. This node will not be used by client apps.
|
||||
/// The wifi/ble radios and the oled screen will be put to sleep.
|
||||
/// This mode may still potentially have higher power usage due to it's preference in message rebroadcasting on the mesh.
|
||||
case router // = 2
|
||||
|
|
@ -217,7 +217,7 @@ struct Config {
|
|||
///
|
||||
/// Repeater device role
|
||||
/// Mesh packets will simply be rebroadcasted over this node. Nodes configured with this role will not originate NodeInfo, Position, Telemetry
|
||||
/// or any other packet type. They will simply rebroadcast any mesh packets on the same frequency, channel num, spread factor, and coding rate.
|
||||
/// or any other packet type. They will simply rebroadcast any mesh packets on the same frequency, channel num, spread factor, and coding rate.
|
||||
case repeater // = 4
|
||||
|
||||
///
|
||||
|
|
@ -508,7 +508,7 @@ struct Config {
|
|||
|
||||
///
|
||||
/// Mesh Super Deep Sleep Timeout Seconds
|
||||
/// While in Light Sleep if this value is exceeded we will lower into super deep sleep
|
||||
/// While in Light Sleep if this value is exceeded we will lower into super deep sleep
|
||||
/// for sds_secs (default 1 year) or a button press
|
||||
/// 0 for default of two hours, MAXUINT for disabled
|
||||
var meshSdsTimeoutSecs: UInt32 = 0
|
||||
|
|
@ -674,7 +674,7 @@ struct Config {
|
|||
var autoScreenCarouselSecs: UInt32 = 0
|
||||
|
||||
///
|
||||
/// If this is set, the displayed compass will always point north. if unset, the old behaviour
|
||||
/// If this is set, the displayed compass will always point north. if unset, the old behaviour
|
||||
/// (top of display is heading direction) is used.
|
||||
var compassNorthTop: Bool = false
|
||||
|
||||
|
|
@ -978,9 +978,9 @@ struct Config {
|
|||
var channelNum: UInt32 = 0
|
||||
|
||||
///
|
||||
/// If true, duty cycle limits will be exceeded and thus you're possibly not following
|
||||
/// If true, duty cycle limits will be exceeded and thus you're possibly not following
|
||||
/// the local regulations if you're not a HAM.
|
||||
/// Has no effect if the duty cycle of the used region is 100%.
|
||||
/// Has no effect if the duty cycle of the used region is 100%.
|
||||
var overrideDutyCycle: Bool = false
|
||||
|
||||
///
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ struct WifiConnectionStatus {
|
|||
mutating func clearStatus() {self._status = nil}
|
||||
|
||||
///
|
||||
/// WiFi access point SSID
|
||||
/// WiFi access point SSID
|
||||
var ssid: String = String()
|
||||
|
||||
///
|
||||
|
|
|
|||
|
|
@ -172,6 +172,13 @@ struct DeviceState {
|
|||
/// Clears the value of `rxWaypoint`. Subsequent reads from it will return its default value.
|
||||
mutating func clearRxWaypoint() {_uniqueStorage()._rxWaypoint = nil}
|
||||
|
||||
///
|
||||
/// The mesh's nodes with their available gpio pins for RemoteHardware module
|
||||
var nodeRemoteHardwarePins: [NodeRemoteHardwarePin] {
|
||||
get {return _storage._nodeRemoteHardwarePins}
|
||||
set {_uniqueStorage()._nodeRemoteHardwarePins = newValue}
|
||||
}
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
|
|
@ -263,11 +270,41 @@ struct OEMStore {
|
|||
fileprivate var _oemLocalModuleConfig: LocalModuleConfig? = nil
|
||||
}
|
||||
|
||||
///
|
||||
/// RemoteHardwarePins associated with a node
|
||||
struct NodeRemoteHardwarePin {
|
||||
// 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.
|
||||
|
||||
///
|
||||
/// The node_num exposing the available gpio pin
|
||||
var nodeNum: UInt32 = 0
|
||||
|
||||
///
|
||||
/// The the available gpio pin for usage with RemoteHardware module
|
||||
var pin: RemoteHardwarePin {
|
||||
get {return _pin ?? RemoteHardwarePin()}
|
||||
set {_pin = newValue}
|
||||
}
|
||||
/// Returns true if `pin` has been explicitly set.
|
||||
var hasPin: Bool {return self._pin != nil}
|
||||
/// Clears the value of `pin`. Subsequent reads from it will return its default value.
|
||||
mutating func clearPin() {self._pin = nil}
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
|
||||
fileprivate var _pin: RemoteHardwarePin? = nil
|
||||
}
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension ScreenFonts: @unchecked Sendable {}
|
||||
extension DeviceState: @unchecked Sendable {}
|
||||
extension ChannelFile: @unchecked Sendable {}
|
||||
extension OEMStore: @unchecked Sendable {}
|
||||
extension NodeRemoteHardwarePin: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
|
@ -294,6 +331,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
|
|||
9: .standard(proto: "no_save"),
|
||||
11: .standard(proto: "did_gps_reset"),
|
||||
12: .standard(proto: "rx_waypoint"),
|
||||
13: .standard(proto: "node_remote_hardware_pins"),
|
||||
]
|
||||
|
||||
fileprivate class _StorageClass {
|
||||
|
|
@ -306,6 +344,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
|
|||
var _noSave: Bool = false
|
||||
var _didGpsReset: Bool = false
|
||||
var _rxWaypoint: MeshPacket? = nil
|
||||
var _nodeRemoteHardwarePins: [NodeRemoteHardwarePin] = []
|
||||
|
||||
static let defaultInstance = _StorageClass()
|
||||
|
||||
|
|
@ -321,6 +360,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
|
|||
_noSave = source._noSave
|
||||
_didGpsReset = source._didGpsReset
|
||||
_rxWaypoint = source._rxWaypoint
|
||||
_nodeRemoteHardwarePins = source._nodeRemoteHardwarePins
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -348,6 +388,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
|
|||
case 9: try { try decoder.decodeSingularBoolField(value: &_storage._noSave) }()
|
||||
case 11: try { try decoder.decodeSingularBoolField(value: &_storage._didGpsReset) }()
|
||||
case 12: try { try decoder.decodeSingularMessageField(value: &_storage._rxWaypoint) }()
|
||||
case 13: try { try decoder.decodeRepeatedMessageField(value: &_storage._nodeRemoteHardwarePins) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -387,6 +428,9 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
|
|||
try { if let v = _storage._rxWaypoint {
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 12)
|
||||
} }()
|
||||
if !_storage._nodeRemoteHardwarePins.isEmpty {
|
||||
try visitor.visitRepeatedMessageField(value: _storage._nodeRemoteHardwarePins, fieldNumber: 13)
|
||||
}
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
|
@ -405,6 +449,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
|
|||
if _storage._noSave != rhs_storage._noSave {return false}
|
||||
if _storage._didGpsReset != rhs_storage._didGpsReset {return false}
|
||||
if _storage._rxWaypoint != rhs_storage._rxWaypoint {return false}
|
||||
if _storage._nodeRemoteHardwarePins != rhs_storage._nodeRemoteHardwarePins {return false}
|
||||
return true
|
||||
}
|
||||
if !storagesAreEqual {return false}
|
||||
|
|
@ -529,3 +574,45 @@ extension OEMStore: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
|
|||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension NodeRemoteHardwarePin: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".NodeRemoteHardwarePin"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .standard(proto: "node_num"),
|
||||
2: .same(proto: "pin"),
|
||||
]
|
||||
|
||||
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.decodeSingularUInt32Field(value: &self.nodeNum) }()
|
||||
case 2: try { try decoder.decodeSingularMessageField(value: &self._pin) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
// The use of inline closures is to circumvent an issue where the compiler
|
||||
// allocates stack space for every if/case branch local when no optimizations
|
||||
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
|
||||
// https://github.com/apple/swift-protobuf/issues/1182
|
||||
if self.nodeNum != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.nodeNum, fieldNumber: 1)
|
||||
}
|
||||
try { if let v = self._pin {
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
|
||||
} }()
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: NodeRemoteHardwarePin, rhs: NodeRemoteHardwarePin) -> Bool {
|
||||
if lhs.nodeNum != rhs.nodeNum {return false}
|
||||
if lhs._pin != rhs._pin {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2318,6 +2318,10 @@ struct DeviceMetadata {
|
|||
/// Device hardware model
|
||||
var hwModel: HardwareModel = .unset
|
||||
|
||||
///
|
||||
/// Has Remote Hardware enabled
|
||||
var hasRemoteHardware_p: Bool = false
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
|
|
@ -4036,6 +4040,7 @@ extension DeviceMetadata: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement
|
|||
7: .same(proto: "role"),
|
||||
8: .standard(proto: "position_flags"),
|
||||
9: .standard(proto: "hw_model"),
|
||||
10: .same(proto: "hasRemoteHardware"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -4053,6 +4058,7 @@ extension DeviceMetadata: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement
|
|||
case 7: try { try decoder.decodeSingularEnumField(value: &self.role) }()
|
||||
case 8: try { try decoder.decodeSingularUInt32Field(value: &self.positionFlags) }()
|
||||
case 9: try { try decoder.decodeSingularEnumField(value: &self.hwModel) }()
|
||||
case 10: try { try decoder.decodeSingularBoolField(value: &self.hasRemoteHardware_p) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -4086,6 +4092,9 @@ extension DeviceMetadata: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement
|
|||
if self.hwModel != .unset {
|
||||
try visitor.visitSingularEnumField(value: self.hwModel, fieldNumber: 9)
|
||||
}
|
||||
if self.hasRemoteHardware_p != false {
|
||||
try visitor.visitSingularBoolField(value: self.hasRemoteHardware_p, fieldNumber: 10)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
|
|
@ -4099,6 +4108,7 @@ extension DeviceMetadata: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement
|
|||
if lhs.role != rhs.role {return false}
|
||||
if lhs.positionFlags != rhs.positionFlags {return false}
|
||||
if lhs.hwModel != rhs.hwModel {return false}
|
||||
if lhs.hasRemoteHardware_p != rhs.hasRemoteHardware_p {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,59 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
typealias Version = _2
|
||||
}
|
||||
|
||||
enum RemoteHardwarePinType: SwiftProtobuf.Enum {
|
||||
typealias RawValue = Int
|
||||
|
||||
///
|
||||
/// Unset/unused
|
||||
case unknown // = 0
|
||||
|
||||
///
|
||||
/// GPIO pin can be read (if it is high / low)
|
||||
case digitalRead // = 1
|
||||
|
||||
///
|
||||
/// GPIO pin can be written to (high / low)
|
||||
case digitalWrite // = 2
|
||||
case UNRECOGNIZED(Int)
|
||||
|
||||
init() {
|
||||
self = .unknown
|
||||
}
|
||||
|
||||
init?(rawValue: Int) {
|
||||
switch rawValue {
|
||||
case 0: self = .unknown
|
||||
case 1: self = .digitalRead
|
||||
case 2: self = .digitalWrite
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
var rawValue: Int {
|
||||
switch self {
|
||||
case .unknown: return 0
|
||||
case .digitalRead: return 1
|
||||
case .digitalWrite: return 2
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if swift(>=4.2)
|
||||
|
||||
extension RemoteHardwarePinType: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
static var allCases: [RemoteHardwarePinType] = [
|
||||
.unknown,
|
||||
.digitalRead,
|
||||
.digitalWrite,
|
||||
]
|
||||
}
|
||||
|
||||
#endif // swift(>=4.2)
|
||||
|
||||
///
|
||||
/// Module Config
|
||||
struct ModuleConfig {
|
||||
|
|
@ -267,6 +320,14 @@ struct ModuleConfig {
|
|||
/// Whether the Module is enabled
|
||||
var enabled: Bool = false
|
||||
|
||||
///
|
||||
/// Whether the Module allows consumers to read / write to pins not defined in available_pins
|
||||
var allowUndefinedPinAccess: Bool = false
|
||||
|
||||
///
|
||||
/// Exposes the available pins to the mesh for reading and writing
|
||||
var availablePins: [RemoteHardwarePin] = []
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
|
|
@ -641,7 +702,7 @@ struct ModuleConfig {
|
|||
var sender: UInt32 = 0
|
||||
|
||||
///
|
||||
/// Bool value indicating that this node should save a RangeTest.csv file.
|
||||
/// Bool value indicating that this node should save a RangeTest.csv file.
|
||||
/// ESP32 Only
|
||||
var save: Bool = false
|
||||
|
||||
|
|
@ -891,7 +952,32 @@ extension ModuleConfig.CannedMessageConfig.InputEventChar: CaseIterable {
|
|||
|
||||
#endif // swift(>=4.2)
|
||||
|
||||
///
|
||||
/// A GPIO pin definition for remote hardware module
|
||||
struct RemoteHardwarePin {
|
||||
// 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.
|
||||
|
||||
///
|
||||
/// GPIO Pin number (must match Arduino)
|
||||
var gpioPin: UInt32 = 0
|
||||
|
||||
///
|
||||
/// Name for the GPIO pin (i.e. Front gate, mailbox, etc)
|
||||
var name: String = String()
|
||||
|
||||
///
|
||||
/// Type of GPIO access available to consumers on the mesh
|
||||
var type: RemoteHardwarePinType = .unknown
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension RemoteHardwarePinType: @unchecked Sendable {}
|
||||
extension ModuleConfig: @unchecked Sendable {}
|
||||
extension ModuleConfig.OneOf_PayloadVariant: @unchecked Sendable {}
|
||||
extension ModuleConfig.MQTTConfig: @unchecked Sendable {}
|
||||
|
|
@ -907,12 +993,21 @@ extension ModuleConfig.RangeTestConfig: @unchecked Sendable {}
|
|||
extension ModuleConfig.TelemetryConfig: @unchecked Sendable {}
|
||||
extension ModuleConfig.CannedMessageConfig: @unchecked Sendable {}
|
||||
extension ModuleConfig.CannedMessageConfig.InputEventChar: @unchecked Sendable {}
|
||||
extension RemoteHardwarePin: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
|
||||
extension RemoteHardwarePinType: SwiftProtobuf._ProtoNameProviding {
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
0: .same(proto: "UNKNOWN"),
|
||||
1: .same(proto: "DIGITAL_READ"),
|
||||
2: .same(proto: "DIGITAL_WRITE"),
|
||||
]
|
||||
}
|
||||
|
||||
extension ModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".ModuleConfig"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
|
|
@ -1187,6 +1282,8 @@ extension ModuleConfig.RemoteHardwareConfig: SwiftProtobuf.Message, SwiftProtobu
|
|||
static let protoMessageName: String = ModuleConfig.protoMessageName + ".RemoteHardwareConfig"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "enabled"),
|
||||
2: .standard(proto: "allow_undefined_pin_access"),
|
||||
3: .standard(proto: "available_pins"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -1196,6 +1293,8 @@ extension ModuleConfig.RemoteHardwareConfig: SwiftProtobuf.Message, SwiftProtobu
|
|||
// enabled. https://github.com/apple/swift-protobuf/issues/1034
|
||||
switch fieldNumber {
|
||||
case 1: try { try decoder.decodeSingularBoolField(value: &self.enabled) }()
|
||||
case 2: try { try decoder.decodeSingularBoolField(value: &self.allowUndefinedPinAccess) }()
|
||||
case 3: try { try decoder.decodeRepeatedMessageField(value: &self.availablePins) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -1205,11 +1304,19 @@ extension ModuleConfig.RemoteHardwareConfig: SwiftProtobuf.Message, SwiftProtobu
|
|||
if self.enabled != false {
|
||||
try visitor.visitSingularBoolField(value: self.enabled, fieldNumber: 1)
|
||||
}
|
||||
if self.allowUndefinedPinAccess != false {
|
||||
try visitor.visitSingularBoolField(value: self.allowUndefinedPinAccess, fieldNumber: 2)
|
||||
}
|
||||
if !self.availablePins.isEmpty {
|
||||
try visitor.visitRepeatedMessageField(value: self.availablePins, fieldNumber: 3)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: ModuleConfig.RemoteHardwareConfig, rhs: ModuleConfig.RemoteHardwareConfig) -> Bool {
|
||||
if lhs.enabled != rhs.enabled {return false}
|
||||
if lhs.allowUndefinedPinAccess != rhs.allowUndefinedPinAccess {return false}
|
||||
if lhs.availablePins != rhs.availablePins {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
|
|
@ -1778,3 +1885,47 @@ extension ModuleConfig.CannedMessageConfig.InputEventChar: SwiftProtobuf._ProtoN
|
|||
27: .same(proto: "BACK"),
|
||||
]
|
||||
}
|
||||
|
||||
extension RemoteHardwarePin: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".RemoteHardwarePin"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .standard(proto: "gpio_pin"),
|
||||
2: .same(proto: "name"),
|
||||
3: .same(proto: "type"),
|
||||
]
|
||||
|
||||
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.decodeSingularUInt32Field(value: &self.gpioPin) }()
|
||||
case 2: try { try decoder.decodeSingularStringField(value: &self.name) }()
|
||||
case 3: try { try decoder.decodeSingularEnumField(value: &self.type) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
if self.gpioPin != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.gpioPin, fieldNumber: 1)
|
||||
}
|
||||
if !self.name.isEmpty {
|
||||
try visitor.visitSingularStringField(value: self.name, fieldNumber: 2)
|
||||
}
|
||||
if self.type != .unknown {
|
||||
try visitor.visitSingularEnumField(value: self.type, fieldNumber: 3)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: RemoteHardwarePin, rhs: RemoteHardwarePin) -> Bool {
|
||||
if lhs.gpioPin != rhs.gpioPin {return false}
|
||||
if lhs.name != rhs.name {return false}
|
||||
if lhs.type != rhs.type {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,10 +124,10 @@ enum PortNum: SwiftProtobuf.Enum {
|
|||
case zpsApp // = 68
|
||||
|
||||
///
|
||||
/// Used to let multiple instances of Linux native applications communicate
|
||||
/// Used to let multiple instances of Linux native applications communicate
|
||||
/// as if they did using their LoRa chip.
|
||||
/// Maintained by GitHub user GUVWAF.
|
||||
/// Project files at https://github.com/GUVWAF/Meshtasticator
|
||||
/// Maintained by GitHub user GUVWAF.
|
||||
/// Project files at https://github.com/GUVWAF/Meshtasticator
|
||||
case simulatorApp // = 69
|
||||
|
||||
///
|
||||
|
|
|
|||
88
Meshtastic/Views/Helpers/BatteryLevelCompact.swift
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
//
|
||||
// BatteryIcon.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright Garth Vander Houwen 3/24/23.
|
||||
//
|
||||
import SwiftUI
|
||||
|
||||
struct BatteryLevelCompact: View {
|
||||
var batteryLevel: Int32?
|
||||
var font: Font
|
||||
var iconFont: Font
|
||||
var color: Color
|
||||
|
||||
var body: some View {
|
||||
|
||||
HStack (alignment: .center, spacing: 0) {
|
||||
if batteryLevel == 100 {
|
||||
|
||||
Image(systemName: "battery.100.bolt")
|
||||
.font(iconFont)
|
||||
.foregroundColor(color)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
} else if batteryLevel! < 100 && batteryLevel! > 74 {
|
||||
|
||||
Image(systemName: "battery.75")
|
||||
.font(iconFont)
|
||||
.foregroundColor(color)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
} else if batteryLevel! < 75 && batteryLevel! > 49 {
|
||||
|
||||
Image(systemName: "battery.50")
|
||||
.font(iconFont)
|
||||
.foregroundColor(color)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
} else if batteryLevel! < 50 && batteryLevel! > 14 {
|
||||
|
||||
Image(systemName: "battery.25")
|
||||
.font(iconFont)
|
||||
.foregroundColor(color)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
} else if batteryLevel! < 15 && batteryLevel! > 0 {
|
||||
|
||||
Image(systemName: "battery.0")
|
||||
.font(iconFont)
|
||||
.foregroundColor(color)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
|
||||
} else if batteryLevel! == 0 {
|
||||
|
||||
Image(systemName: "battery.0")
|
||||
.font(iconFont)
|
||||
.foregroundColor(.red)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
} else if batteryLevel! > 100 {
|
||||
|
||||
Image(systemName: "powerplug")
|
||||
.font(iconFont)
|
||||
.foregroundColor(color)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
}
|
||||
|
||||
if batteryLevel ?? 0 > 100 {
|
||||
Text("PWD")
|
||||
.font(font)
|
||||
} else if batteryLevel == 100 {
|
||||
Text("CHG")
|
||||
.font(font)
|
||||
} else {
|
||||
Text("\(batteryLevel ?? 0)%")
|
||||
.font(font)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct BatteryLevelCompact_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
VStack {
|
||||
BatteryLevelCompact(batteryLevel: 111, font: .footnote, iconFont: .callout, color: Color.accentColor)
|
||||
BatteryLevelCompact(batteryLevel: 100, font: .footnote, iconFont: .callout, color: Color.accentColor)
|
||||
BatteryLevelCompact(batteryLevel: 99, font: .footnote, iconFont: .callout, color: Color.accentColor)
|
||||
BatteryLevelCompact(batteryLevel: 74, font: .footnote, iconFont: .callout, color: Color.accentColor)
|
||||
BatteryLevelCompact(batteryLevel: 49, font: .footnote, iconFont: .callout, color: Color.accentColor)
|
||||
BatteryLevelCompact(batteryLevel: 14, font: .footnote, iconFont: .callout, color: Color.accentColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
96
Meshtastic/Views/Helpers/LoRaSignalStrength.swift
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
//
|
||||
// LoRaSignalStrength.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Created by Garth Vander Houwen on 5/15/23.
|
||||
//
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct LoRaSignalStrengthMeter: View {
|
||||
|
||||
var snr: Float
|
||||
var rssi: Int32
|
||||
var preset: ModemPresets
|
||||
var compact: Bool
|
||||
|
||||
var body: some View {
|
||||
|
||||
let signalStrength = getLoRaSignalStrength(snr: snr, rssi: rssi, preset: preset)
|
||||
let gradient = Gradient(colors: [.red, .orange, .yellow, .green])
|
||||
|
||||
if !compact {
|
||||
VStack {
|
||||
LoRaSignalStrengthIndicator(signalStrength: signalStrength)
|
||||
Text("Signal \(signalStrength.description)").font(.footnote)
|
||||
Text("SNR \(String(format: "%.2f", snr))dB")
|
||||
.foregroundColor(getSnrColor(snr: snr, preset: ModemPresets.longFast))
|
||||
.font(.caption2)
|
||||
Text("RSSI \(rssi)dB")
|
||||
.foregroundColor(getRssiColor(rssi: rssi))
|
||||
.font(.caption2)
|
||||
}
|
||||
} else {
|
||||
Gauge(value: Double(signalStrength.rawValue), in: 0...3) {
|
||||
} currentValueLabel: {
|
||||
Image(systemName: "dot.radiowaves.left.and.right")
|
||||
.font(.caption)
|
||||
Text("Signal \(signalStrength.description)")
|
||||
.font(.caption)
|
||||
}
|
||||
.gaugeStyle(.accessoryLinear)
|
||||
.tint(gradient)
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct LoRaSignalStrengthMeter_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ScrollView {
|
||||
VStack {
|
||||
HStack {
|
||||
// Good
|
||||
LoRaSignalStrengthMeter(snr: -10, rssi: -100, preset: ModemPresets.longFast, compact: false)
|
||||
LoRaSignalStrengthMeter(snr: -17, rssi: -100, preset: ModemPresets.longFast, compact: false)
|
||||
}
|
||||
HStack {
|
||||
// Fair
|
||||
LoRaSignalStrengthMeter(snr: -9.5, rssi: -119, preset: ModemPresets.longFast, compact: false)
|
||||
LoRaSignalStrengthMeter(snr: -17.5, rssi: -100, preset: ModemPresets.longFast, compact: false)
|
||||
}
|
||||
HStack {
|
||||
// Bad
|
||||
LoRaSignalStrengthMeter(snr: -11.25, rssi: -120, preset: ModemPresets.longFast, compact: false)
|
||||
LoRaSignalStrengthMeter(snr: -12.75, rssi: -139, preset: ModemPresets.longFast, compact: false)
|
||||
}
|
||||
HStack {
|
||||
LoRaSignalStrengthMeter(snr: -20.25, rssi: -128, preset: ModemPresets.longFast, compact: false)
|
||||
LoRaSignalStrengthMeter(snr: -30, rssi: -120, preset: ModemPresets.longFast, compact: false)
|
||||
}
|
||||
HStack {
|
||||
LoRaSignalStrengthMeter(snr: -15, rssi: -124, preset: ModemPresets.longFast, compact: false)
|
||||
LoRaSignalStrengthMeter(snr: -17.25, rssi: -126, preset: ModemPresets.longFast, compact: false)
|
||||
LoRaSignalStrengthMeter(snr: -19.5, rssi: -128, preset: ModemPresets.longFast, compact: false)
|
||||
}
|
||||
HStack {
|
||||
// None
|
||||
LoRaSignalStrengthMeter(snr: -26.0, rssi: -129, preset: ModemPresets.longFast, compact: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VStack {
|
||||
// Good
|
||||
LoRaSignalStrengthMeter(snr: -10, rssi: -100, preset: ModemPresets.longFast, compact: true)
|
||||
// Fair
|
||||
LoRaSignalStrengthMeter(snr: -9.5, rssi: -119, preset: ModemPresets.longFast, compact: true)
|
||||
// Bad
|
||||
LoRaSignalStrengthMeter(snr: -12.75, rssi: -139, preset: ModemPresets.longFast, compact: true)
|
||||
// None
|
||||
LoRaSignalStrengthMeter(snr: -26.0, rssi: -128, preset: ModemPresets.longFast, compact: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -5,8 +5,6 @@
|
|||
// Copyright Garth Vander Houwen 5/9/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
|
|
@ -18,22 +16,25 @@ struct LoRaSignalStrengthIndicator: View {
|
|||
ForEach(0..<3) { bar in
|
||||
RoundedRectangle(cornerRadius: 3)
|
||||
.divided(amount: (CGFloat(bar) + 1) / CGFloat(3))
|
||||
.fill(getColor().opacity(bar <= signalStrength.rawValue ? 1 : 0.3))
|
||||
.fill(getColor(signalStrength: signalStrength).opacity(bar <= signalStrength.rawValue ? 1 : 0.3))
|
||||
.frame(width: 8, height: 40)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func getColor() -> Color {
|
||||
switch signalStrength {
|
||||
case .none:
|
||||
return Color.red
|
||||
case .bad:
|
||||
return Color.orange
|
||||
case .fair:
|
||||
return Color.yellow
|
||||
case .good:
|
||||
return Color.green
|
||||
struct LoRaSignalStrengthIndicator_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
VStack {
|
||||
let signalStrength = getLoRaSignalStrength(snr: -12.75, rssi: -139, preset: ModemPresets.longFast)
|
||||
LoRaSignalStrengthIndicator(signalStrength: signalStrength)
|
||||
Text("Signal \(signalStrength.description)").font(.footnote)
|
||||
Text("SNR \(String(format: "%.2f", -12.75))dB")
|
||||
.foregroundColor(getSnrColor(snr: -12.75, preset: ModemPresets.longFast))
|
||||
.font(.caption2)
|
||||
Text("RSSI \(-139)dB")
|
||||
.foregroundColor(getRssiColor(rssi: -139))
|
||||
.font(.caption2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -57,13 +58,26 @@ enum LoRaSignalStrength: Int {
|
|||
}
|
||||
}
|
||||
|
||||
func getLoRaSignalStrength(snr: Float, rssi: Int32) -> LoRaSignalStrength {
|
||||
private func getColor(signalStrength: LoRaSignalStrength) -> Color {
|
||||
switch signalStrength {
|
||||
case .none:
|
||||
return Color.red
|
||||
case .bad:
|
||||
return Color.orange
|
||||
case .fair:
|
||||
return Color.yellow
|
||||
case .good:
|
||||
return Color.green
|
||||
}
|
||||
}
|
||||
|
||||
func getLoRaSignalStrength(snr: Float, rssi: Int32, preset: ModemPresets) -> LoRaSignalStrength {
|
||||
|
||||
if rssi > -115 && snr > -7 {
|
||||
if rssi > -115 && snr > (preset.snrLimit()) {
|
||||
return .good
|
||||
} else if rssi < -126 && snr < -15 {
|
||||
} else if rssi < -126 && snr < (preset.snrLimit() - 7.5) {
|
||||
return .none
|
||||
} else if rssi <= -120 || snr <= -13 {
|
||||
} else if rssi <= -120 || snr <= (preset.snrLimit() - 5.5) {
|
||||
return .bad
|
||||
} else {
|
||||
return .fair
|
||||
|
|
@ -86,14 +100,14 @@ func getRssiColor(rssi: Int32) -> Color {
|
|||
}
|
||||
}
|
||||
|
||||
func getSnrColor(snr: Float) -> Color {
|
||||
if snr > -7 {
|
||||
func getSnrColor(snr: Float, preset: ModemPresets) -> Color {
|
||||
if snr > preset.snrLimit() {
|
||||
/// Good
|
||||
return .green
|
||||
} else if snr < -7 && snr > -13 {
|
||||
} else if snr < preset.snrLimit() && snr > (preset.snrLimit() - 5.5) {
|
||||
/// Fair
|
||||
return .yellow
|
||||
} else if snr >= -14 {
|
||||
} else if snr >= (preset.snrLimit() - 7.5) {
|
||||
/// Bad
|
||||
return .orange
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -47,11 +47,11 @@ struct NodeInfoView: View {
|
|||
Divider()
|
||||
if node.snr != 0 {
|
||||
VStack(alignment: .center) {
|
||||
let signalStrength = getLoRaSignalStrength(snr: node.snr, rssi: node.rssi)
|
||||
let signalStrength = getLoRaSignalStrength(snr: node.snr, rssi: node.rssi, preset: ModemPresets.longModerate)
|
||||
LoRaSignalStrengthIndicator(signalStrength: signalStrength)
|
||||
Text("Signal \(signalStrength.description)").font(.title)
|
||||
Text("SNR \(String(format: "%.2f", node.snr))dB")
|
||||
.foregroundColor(getSnrColor(snr: node.snr))
|
||||
.foregroundColor(getSnrColor(snr: node.snr, preset: ModemPresets.longModerate))
|
||||
.font(.title3)
|
||||
Text("RSSI \(node.rssi)dB")
|
||||
.foregroundColor(getRssiColor(rssi: node.rssi))
|
||||
|
|
@ -156,11 +156,11 @@ struct NodeInfoView: View {
|
|||
if node.snr != 0 {
|
||||
Divider()
|
||||
VStack(alignment: .center) {
|
||||
let signalStrength = getLoRaSignalStrength(snr: node.snr, rssi: node.rssi)
|
||||
let signalStrength = getLoRaSignalStrength(snr: node.snr, rssi: node.rssi, preset: ModemPresets.longModerate)
|
||||
LoRaSignalStrengthIndicator(signalStrength: signalStrength)
|
||||
Text("Signal \(signalStrength.description)").font(.footnote)
|
||||
Text("SNR \(String(format: "%.2f", node.snr))dB")
|
||||
.foregroundColor(getSnrColor(snr: node.snr))
|
||||
.foregroundColor(getSnrColor(snr: node.snr, preset: ModemPresets.longModerate))
|
||||
.font(.caption2)
|
||||
Text("RSSI \(node.rssi)dB")
|
||||
.foregroundColor(getRssiColor(rssi: node.rssi))
|
||||
|
|
|
|||
|
|
@ -4,9 +4,18 @@
|
|||
//
|
||||
// Copyright(c) Josh Pirihi & Garth Vander Houwen 1/16/22.
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import MapKit
|
||||
|
||||
struct PolygonInfo: Codable {
|
||||
let stroke: String?
|
||||
let strokeWidth, strokeOpacity: Int?
|
||||
let fill: String?
|
||||
let fillOpacity: Double?
|
||||
let title, subtitle: String?
|
||||
}
|
||||
|
||||
func degreesToRadians(_ number: Double) -> Double {
|
||||
return number * .pi / 180
|
||||
}
|
||||
|
|
@ -29,7 +38,7 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
let showRouteLines: Bool
|
||||
|
||||
let mapViewType: MKMapType = MKMapType.standard
|
||||
|
||||
|
||||
// Offline Map Tiles
|
||||
@AppStorage("lastUpdatedLocalMapFile") private var lastUpdatedLocalMapFile = 0
|
||||
@State private var loadedLastUpdatedLocalMapFile = 0
|
||||
|
|
@ -65,6 +74,7 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
}
|
||||
// Other MKMapView Settings
|
||||
mapView.preferredConfiguration.elevationStyle = .realistic// .flat
|
||||
mapView.pointOfInterestFilter = MKPointOfInterestFilter.excludingAll
|
||||
mapView.isPitchEnabled = true
|
||||
mapView.isRotateEnabled = true
|
||||
mapView.isScrollEnabled = true
|
||||
|
|
@ -134,14 +144,7 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
}
|
||||
}
|
||||
|
||||
func makeUIView(context: Context) -> MKMapView {
|
||||
currentMapLayer = nil
|
||||
mapView.delegate = context.coordinator
|
||||
self.configureMap(mapView: mapView)
|
||||
return mapView
|
||||
}
|
||||
|
||||
func updateUIView(_ mapView: MKMapView, context: Context) {
|
||||
private func setMbtilesOverlay(mapView: MKMapView) {
|
||||
|
||||
// MBTiles Offline
|
||||
if UserDefaults.enableOfflineMaps && UserDefaults.enableOfflineMapsMBTiles {
|
||||
|
|
@ -149,7 +152,7 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
if self.customMapOverlay != self.presentCustomMapOverlayHash || self.loadedLastUpdatedLocalMapFile != self.lastUpdatedLocalMapFile {
|
||||
mapView.removeOverlays(mapView.overlays)
|
||||
if self.customMapOverlay != nil {
|
||||
|
||||
|
||||
let fileManager = FileManager.default
|
||||
let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
|
||||
let tilePath = documentsDirectory.appendingPathComponent("offline_map.mbtiles", isDirectory: false).path
|
||||
|
|
@ -169,15 +172,69 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func setGeoJsonOverlay(mapView: MKMapView) {
|
||||
|
||||
guard let geoJsonFileUrl = URL(string: "https://raw.githubusercontent.com/johan/world.geo.json/master/countries.geo.json"), // Bundle.main.url(forResource: "location", withExtension: "geojson"),
|
||||
//guard let geoJsonFileUrl = URL(string: "https://hrbrmstr.github.io/noaa-alerts-sp-to-geojson/current-all.geojson"),
|
||||
let geoJsonData = try? Data.init(contentsOf: geoJsonFileUrl) else {
|
||||
fatalError("Failure to fetch the file.")
|
||||
}
|
||||
guard let objs = try? MKGeoJSONDecoder().decode(geoJsonData) as? [MKGeoJSONFeature] else {
|
||||
fatalError("Wrong format")
|
||||
}
|
||||
// Parse the objects
|
||||
objs.forEach { (feature) in
|
||||
guard let geometry = feature.geometry.first,
|
||||
let propData = feature.properties else {
|
||||
return;
|
||||
}
|
||||
// Check if it is MKPolygon
|
||||
if let polygon = geometry as? MKPolygon {
|
||||
let polygonInfo = try? JSONDecoder.init().decode(PolygonInfo.self, from: propData)
|
||||
mapView.addOverlay(polygon)
|
||||
//self.view?.render(overlay: polygon, info: polygonInfo)
|
||||
}
|
||||
// Check if it is MKPolyline
|
||||
if let polyline = geometry as? MKPolyline {
|
||||
mapView.addOverlay(polyline, level: .aboveLabels)
|
||||
//let polylineInfo = try? JSONDecoder.init().decode(PolylineInfo.self, from: propData)
|
||||
//self.view?.render(overlay: polyline, info: polylineInfo)
|
||||
}
|
||||
|
||||
// Check if it is MKPointAnnotation
|
||||
// if let annotation = geometry as? MKPointAnnotation {
|
||||
// let info = try? JSONDecoder.init().decode(Info.self, from: propData)
|
||||
// let storeAnnotation = StoreAnnotation.init(title: info?.name,
|
||||
// subtitle: info?.subTitle,
|
||||
// website: info?.website,
|
||||
// coordinate: annotation.coordinate)
|
||||
// self.view?.setAnnotations(annotations: [storeAnnotation])
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
func makeUIView(context: Context) -> MKMapView {
|
||||
currentMapLayer = nil
|
||||
mapView.delegate = context.coordinator
|
||||
self.configureMap(mapView: mapView)
|
||||
return mapView
|
||||
}
|
||||
|
||||
func updateUIView(_ mapView: MKMapView, context: Context) {
|
||||
|
||||
// Set MBTiles overlay layer
|
||||
setMbtilesOverlay(mapView: mapView)
|
||||
// Set selected map base layer
|
||||
setMapBaseLayer(mapView: mapView)
|
||||
// Set map overlay layer
|
||||
// Set map tile server and weather overlay layers
|
||||
setMapOverlays(mapView: mapView)
|
||||
|
||||
let latest = positions
|
||||
.filter { $0.latest == true }
|
||||
.sorted { $0.nodePosition?.num ?? 0 > $1.nodePosition?.num ?? -1 }
|
||||
|
||||
|
||||
// Node Route Lines
|
||||
if showRouteLines {
|
||||
// Remove all existing PolyLine Overlays
|
||||
|
|
@ -437,7 +494,14 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
renderer.lineWidth = 8
|
||||
return renderer
|
||||
}
|
||||
return MKOverlayRenderer()
|
||||
if let polygon = overlay as? MKPolygon {
|
||||
let renderer = MKPolygonRenderer(polygon: polygon)
|
||||
renderer.fillColor = UIColor.purple.withAlphaComponent(0.2)
|
||||
renderer.strokeColor = .purple.withAlphaComponent(0.7)
|
||||
|
||||
return renderer
|
||||
}
|
||||
return MKOverlayRenderer(overlay: overlay)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ struct NodeDetail: View {
|
|||
var body: some View {
|
||||
|
||||
let hwModelString = node.user?.hwModel ?? "UNSET"
|
||||
|
||||
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? -1, context: context)
|
||||
NavigationStack {
|
||||
GeometryReader { bounds in
|
||||
VStack {
|
||||
|
|
@ -84,19 +84,21 @@ struct NodeDetail: View {
|
|||
VStack(alignment: .leading) {
|
||||
Spacer()
|
||||
HStack(alignment: .bottom, spacing: 1) {
|
||||
|
||||
// Picker("Map Type", selection: $mapType) {
|
||||
// ForEach(MeshMapTypes.allCases) { map in
|
||||
// Text(map.description)
|
||||
// .tag(map.MKMapTypeValue())
|
||||
// }
|
||||
// }
|
||||
// .onChange(of: (mapType)) { newMapType in
|
||||
// UserDefaults.mapType = Int(newMapType.rawValue)
|
||||
// }
|
||||
// .background(.thinMaterial, in: RoundedRectangle(cornerRadius: 12, style: .continuous))
|
||||
// .pickerStyle(.menu)
|
||||
// .padding(5)
|
||||
Picker("Map Type", selection: $selectedMapLayer) {
|
||||
ForEach(MapLayer.allCases, id: \.self) { layer in
|
||||
if layer == MapLayer.offline && UserDefaults.enableOfflineMaps {
|
||||
Text(layer.localized)
|
||||
} else if layer != MapLayer.offline {
|
||||
Text(layer.localized)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: (selectedMapLayer)) { newMapLayer in
|
||||
UserDefaults.mapLayer = newMapLayer
|
||||
}
|
||||
.background(.thinMaterial, in: RoundedRectangle(cornerRadius: 12, style: .continuous))
|
||||
.pickerStyle(.menu)
|
||||
.padding(5)
|
||||
VStack {
|
||||
Label(temperature?.formatted(.measurement(width: .narrow)) ?? "??", systemImage: symbolName)
|
||||
.font(.caption)
|
||||
|
|
@ -151,7 +153,7 @@ struct NodeDetail: View {
|
|||
if self.bleManager.connectedPeripheral != nil && node.metadata != nil {
|
||||
|
||||
HStack {
|
||||
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? -1, context: context)
|
||||
|
||||
if node.metadata?.canShutdown ?? false {
|
||||
|
||||
Button(action: {
|
||||
|
|
|
|||
|
|
@ -27,22 +27,31 @@ struct NodeList: View {
|
|||
var body: some View {
|
||||
|
||||
NavigationSplitView {
|
||||
let connectedNodeNum = Int(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral?.num ?? 0 : 0)
|
||||
let connectedNode = nodes.first(where: { $0.num == connectedNodeNum })
|
||||
List(nodes, id: \.self, selection: $selection) { node in
|
||||
if nodes.count == 0 {
|
||||
Text("no.nodes").font(.title)
|
||||
} else {
|
||||
NavigationLink(value: node) {
|
||||
let connected: Bool = (bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral?.num ?? -1 == node.num)
|
||||
VStack(alignment: .leading) {
|
||||
LazyVStack(alignment: .leading) {
|
||||
HStack {
|
||||
CircleText(text: node.user?.shortName ?? "???", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 65, fontSize: (node.user?.shortName ?? "???").isEmoji() ? 44 : 22, brightness: 0.0, textColor: UIColor(hex: UInt32(node.num)).isLight() ? .black : .white)
|
||||
.padding(.trailing, 5)
|
||||
VStack(alignment: .leading) {
|
||||
CircleText(text: node.user?.shortName ?? "???", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 65, fontSize: (node.user?.shortName ?? "???").isEmoji() ? 44 : 22, brightness: 0.0, textColor: UIColor(hex: UInt32(node.num)).isLight() ? .black : .white)
|
||||
.padding(.trailing, 5)
|
||||
let deviceMetrics = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0"))
|
||||
if deviceMetrics?.count ?? 0 >= 1 {
|
||||
let mostRecent = deviceMetrics?.lastObject as? TelemetryEntity
|
||||
BatteryLevelCompact(batteryLevel: mostRecent?.batteryLevel, font: .caption2, iconFont: .callout, color: .accentColor)
|
||||
}
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
Text(node.user?.longName ?? "unknown".localized)
|
||||
.fontWeight(.medium)
|
||||
.font(.callout)
|
||||
if connected {
|
||||
HStack {
|
||||
HStack(alignment: .bottom) {
|
||||
Image(systemName: "repeat.circle.fill")
|
||||
.font(.callout)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
|
|
@ -80,6 +89,11 @@ struct NodeList: View {
|
|||
LastHeardText(lastHeard: node.lastHeard)
|
||||
.font(.caption)
|
||||
}
|
||||
if !connected {
|
||||
HStack(alignment: .bottom) { let preset = ModemPresets(rawValue: Int(connectedNode?.loRaConfig?.modemPreset ?? 0))
|
||||
LoRaSignalStrengthMeter(snr: node.snr, rssi: node.rssi, preset: preset ?? ModemPresets.longFast, compact: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -178,14 +178,14 @@ struct NodeMap: View {
|
|||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
.onChange(of: (enableOfflineMaps)) { newEnableOfflineMaps in
|
||||
UserDefaults.enableOfflineMaps = newEnableOfflineMaps
|
||||
if !newEnableOfflineMaps {
|
||||
UserDefaults.enableOfflineMaps = enableOfflineMaps
|
||||
if !enableOfflineMaps {
|
||||
if self.selectedMapLayer == .offline {
|
||||
self.selectedMapLayer = .standard
|
||||
}
|
||||
}
|
||||
}
|
||||
if UserDefaults.enableOfflineMaps {
|
||||
if enableOfflineMaps {
|
||||
VStack (alignment: .leading) {
|
||||
|
||||
if !enableOfflineMapsMBTiles {
|
||||
|
|
|
|||
|
|
@ -262,54 +262,42 @@ struct DeviceConfig: View {
|
|||
}
|
||||
}
|
||||
.onChange(of: deviceRole) { newRole in
|
||||
|
||||
if node != nil && node!.deviceConfig != nil {
|
||||
|
||||
if node != nil && node?.deviceConfig != nil {
|
||||
if newRole != node!.deviceConfig!.role { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: serialEnabled) { newSerial in
|
||||
|
||||
if node != nil && node!.deviceConfig != nil {
|
||||
|
||||
if node != nil && node?.deviceConfig != nil {
|
||||
if newSerial != node!.deviceConfig!.serialEnabled { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: debugLogEnabled) { newDebugLog in
|
||||
|
||||
if node != nil && node!.deviceConfig != nil {
|
||||
|
||||
if node != nil && node?.deviceConfig != nil {
|
||||
if newDebugLog != node!.deviceConfig!.debugLogEnabled { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: buttonGPIO) { newButtonGPIO in
|
||||
|
||||
if node != nil && node!.deviceConfig != nil {
|
||||
|
||||
if node != nil && node?.deviceConfig != nil {
|
||||
if newButtonGPIO != node!.deviceConfig!.buttonGpio { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: buzzerGPIO) { newBuzzerGPIO in
|
||||
|
||||
if node != nil && node!.deviceConfig != nil {
|
||||
|
||||
if node != nil && node?.deviceConfig != nil {
|
||||
if newBuzzerGPIO != node!.deviceConfig!.buttonGpio { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: rebroadcastMode) { newRebroadcastMode in
|
||||
|
||||
if node != nil && node!.deviceConfig != nil {
|
||||
|
||||
if node != nil && node?.deviceConfig != nil {
|
||||
if newRebroadcastMode != node!.deviceConfig!.rebroadcastMode { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: doubleTapAsButtonPress) { newDoubleTapAsButtonPress in
|
||||
if node != nil && node!.deviceConfig != nil {
|
||||
if node != nil && node?.deviceConfig != nil {
|
||||
if newDoubleTapAsButtonPress != node!.deviceConfig!.doubleTapAsButtonPress { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: isManaged) { newIsManaged in
|
||||
if node != nil && node!.deviceConfig != nil {
|
||||
if node != nil && node?.deviceConfig != nil {
|
||||
if newIsManaged != node!.deviceConfig!.isManaged { hasChanges = true }
|
||||
}
|
||||
}
|
||||
|
|
|
|||