From 93ffd8c72d4336640bbcfc63597464b8eb04c49e Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Fri, 24 Mar 2023 10:35:54 -0700 Subject: [PATCH] Fix username on mqtt config save Set mqtt.address character limit to 62 --- Meshtastic.xcodeproj/project.pbxproj | 6 ++ Meshtastic/Persistence/UpdateCoreData.swift | 4 +- .../Settings/Config/Module/MQTTConfig.swift | 4 +- Meshtastic/Views/Settings/ShareChannels.swift | 2 +- Widgets/BatteryLevel.swift | 76 +++++++++++++++++++ Widgets/WidgetsLiveActivity.swift | 73 +++++++++++------- 6 files changed, 133 insertions(+), 32 deletions(-) create mode 100644 Widgets/BatteryLevel.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 1353fce8..bdb16180 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -108,6 +108,8 @@ DDC2E1A726CEB3400042C5E4 /* LocationHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E1A626CEB3400042C5E4 /* LocationHelper.swift */; }; DDC3B274283F411B00AC321C /* LastHeardText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC3B273283F411B00AC321C /* LastHeardText.swift */; }; DDC4D568275499A500A4208E /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC4D567275499A500A4208E /* Persistence.swift */; }; + DDC94FC129CE063B0082EA6E /* BatteryLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC94FC029CE063B0082EA6E /* BatteryLevel.swift */; }; + DDC94FC229CE063B0082EA6E /* BatteryLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC94FC029CE063B0082EA6E /* BatteryLevel.swift */; }; DDCDC6CB29481FCC004C1DDA /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DDCDC6CD29481FCC004C1DDA /* Localizable.strings */; }; DDCE4E2C2869F92900BE9F8F /* UserConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCE4E2B2869F92900BE9F8F /* UserConfig.swift */; }; DDD3BBD5292D763200D609B3 /* MeshtasticTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD3BBD4292D763200D609B3 /* MeshtasticTests.swift */; }; @@ -282,6 +284,7 @@ DDC2E1A626CEB3400042C5E4 /* LocationHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationHelper.swift; sourceTree = ""; }; DDC3B273283F411B00AC321C /* LastHeardText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LastHeardText.swift; sourceTree = ""; }; DDC4D567275499A500A4208E /* Persistence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = ""; }; + DDC94FC029CE063B0082EA6E /* BatteryLevel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryLevel.swift; sourceTree = ""; }; DDCDC69A29467643004C1DDA /* MeshtasticDataModelV3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV3.xcdatamodel; sourceTree = ""; }; DDCDC6CC29481FCC004C1DDA /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; DDCDC6CE294821AD004C1DDA /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; @@ -677,6 +680,7 @@ DDDE59FC29AF163D00490C6C /* Widgets.swift */, DDDE5A0029AF163E00490C6C /* Info.plist */, DDDE5A0F29AFE69700490C6C /* MeshActivityAttributes.swift */, + DDC94FC029CE063B0082EA6E /* BatteryLevel.swift */, ); path = Widgets; sourceTree = ""; @@ -901,6 +905,7 @@ DD90860E26F69BAE00DC5189 /* NodeMap.swift in Sources */, DD964FBD296E6B01007C176F /* EmojiOnlyTextField.swift in Sources */, DD8169FF272476C700F4AB02 /* LogDocument.swift in Sources */, + DDC94FC129CE063B0082EA6E /* BatteryLevel.swift in Sources */, DD2AD8A8296D2DF9001FF0E7 /* MapViewSwiftUI.swift in Sources */, DD5E5213298EE33B00D21B61 /* deviceonly.pb.swift in Sources */, DD5E5208298EE33B00D21B61 /* rtttl.pb.swift in Sources */, @@ -1003,6 +1008,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + DDC94FC229CE063B0082EA6E /* BatteryLevel.swift in Sources */, DDDE5A1129AFE69700490C6C /* MeshActivityAttributes.swift in Sources */, DDDE59FB29AF163D00490C6C /* WidgetsLiveActivity.swift in Sources */, DDDE59FD29AF163D00490C6C /* Widgets.swift in Sources */, diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index 9763b0e7..639a2bb8 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -719,7 +719,7 @@ func upsertMqttModuleConfigPacket(config: Meshtastic.ModuleConfig.MQTTConfig, no let newMQTTConfig = MQTTConfigEntity(context: context) newMQTTConfig.enabled = config.enabled newMQTTConfig.address = config.address - newMQTTConfig.address = config.username + newMQTTConfig.username = config.username newMQTTConfig.password = config.password newMQTTConfig.encryptionEnabled = config.encryptionEnabled newMQTTConfig.jsonEnabled = config.jsonEnabled @@ -727,7 +727,7 @@ func upsertMqttModuleConfigPacket(config: Meshtastic.ModuleConfig.MQTTConfig, no } else { fetchedNode[0].mqttConfig?.enabled = config.enabled fetchedNode[0].mqttConfig?.address = config.address - fetchedNode[0].mqttConfig?.address = config.username + fetchedNode[0].mqttConfig?.username = config.username fetchedNode[0].mqttConfig?.password = config.password fetchedNode[0].mqttConfig?.encryptionEnabled = config.encryptionEnabled fetchedNode[0].mqttConfig?.jsonEnabled = config.jsonEnabled diff --git a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift index 33a57063..4cefda0c 100644 --- a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift @@ -79,8 +79,8 @@ struct MQTTConfig: View { .onChange(of: address, perform: { _ in let totalBytes = address.utf8.count // Only mess with the value if it is too big - if totalBytes > 30 { - let firstNBytes = Data(username.utf8.prefix(30)) + if totalBytes > 62 { + let firstNBytes = Data(username.utf8.prefix(62)) if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) { // Set the shortName back to the last place where it was the right size address = maxBytesString diff --git a/Meshtastic/Views/Settings/ShareChannels.swift b/Meshtastic/Views/Settings/ShareChannels.swift index 5ca297dd..86d79877 100644 --- a/Meshtastic/Views/Settings/ShareChannels.swift +++ b/Meshtastic/Views/Settings/ShareChannels.swift @@ -224,7 +224,7 @@ struct ShareChannels: View { .font(.headline) .padding(.bottom) Text("Primary Channel").font(.title2) - Text("The first channel is the Primary channel and is where much of the mesh activity takes place. DM's are only available on the primary channel and it can not be disabled.") + Text("The first channel is the Primary channel and is where much of the mesh activity takes place. DM's are only available on the primary channel and it can not be disabled. If you don't share your primary channel, the first channel will become the primary channel on the other network and will allow communication with your mesh on the group channel.") .font(.callout) .padding([.leading, .trailing, .bottom]) Text("Admin Channel").font(.title2) diff --git a/Widgets/BatteryLevel.swift b/Widgets/BatteryLevel.swift new file mode 100644 index 00000000..f20d03aa --- /dev/null +++ b/Widgets/BatteryLevel.swift @@ -0,0 +1,76 @@ +// +// BatteryLevel.swift +// Meshtastic +// +// Copyright Garth Vander Houwen 3/24/23. +// +import SwiftUI + +struct BatteryIcon: View { + var batteryLevel: Int32? + var font: Font + var color: Color + + var body: some View { + + if batteryLevel == 100 { + + Image(systemName: "battery.100.bolt") + .font(font) + .foregroundColor(color) + .symbolRenderingMode(.hierarchical) + } else if batteryLevel! < 100 && batteryLevel! > 74 { + + Image(systemName: "battery.75") + .font(font) + .foregroundColor(color) + .symbolRenderingMode(.hierarchical) + } else if batteryLevel! < 75 && batteryLevel! > 49 { + + Image(systemName: "battery.50") + .font(font) + .foregroundColor(color) + .symbolRenderingMode(.hierarchical) + } else if batteryLevel! < 50 && batteryLevel! > 14 { + + Image(systemName: "battery.25") + .font(font) + .foregroundColor(color) + .symbolRenderingMode(.hierarchical) + } else if batteryLevel! < 15 && batteryLevel! >= 0 { + + Image(systemName: "battery.0") + .font(font) + .foregroundColor(.red) + .symbolRenderingMode(.hierarchical) + } else if batteryLevel! > 100 { + + Image(systemName: "powerplug") + .font(font) + .foregroundColor(color) + .symbolRenderingMode(.hierarchical) + } + else { + + Image(systemName: "battery.0") + .font(font) + .foregroundColor(color) + .symbolRenderingMode(.hierarchical) + } + } +} + +struct BatteryIcon_Previews: PreviewProvider { + static var previews: some View { + BatteryIcon(batteryLevel: 100, font: .title2, color: Color.accentColor) + .previewLayout(.fixed(width: 75, height: 75)) + BatteryIcon(batteryLevel: 99, font: .title2, color: Color.accentColor) + .previewLayout(.fixed(width: 75, height: 75)) + BatteryIcon(batteryLevel: 74, font: .title2, color: Color.accentColor) + .previewLayout(.fixed(width: 75, height: 75)) + BatteryIcon(batteryLevel: 49, font: .title2, color: Color.accentColor) + .previewLayout(.fixed(width: 75, height: 75)) + BatteryIcon(batteryLevel: 14, font: .title2, color: Color.accentColor) + .previewLayout(.fixed(width: 75, height: 75)) + } +} diff --git a/Widgets/WidgetsLiveActivity.swift b/Widgets/WidgetsLiveActivity.swift index 835ecbfa..92f246fa 100644 --- a/Widgets/WidgetsLiveActivity.swift +++ b/Widgets/WidgetsLiveActivity.swift @@ -20,22 +20,49 @@ struct WidgetsLiveActivity: Widget { } dynamicIsland: { context in DynamicIsland { DynamicIslandExpandedRegion(.leading) { - NodeInfoView(nodeName: context.attributes.name, timerRange: context.state.timerRange, channelUtilization: context.state.channelUtilization, airtime: context.state.airtime, batteryLevel: context.state.batteryLevel) - .tint(Color("LightIndigo")) - .padding(.top) + Text("Network") + .font(.headline) + .fontWeight(.bold) + .foregroundStyle(.secondary) + .fixedSize() + .padding(.top, 10) + Text("\(String(format: "Ch. Util: %.2f", context.state.channelUtilization))%") + .font(.headline) + .fontWeight(.medium) + .foregroundStyle(.secondary) + .fixedSize() + Text("\(String(format: "Airtime: %.2f", context.state.airtime))%") + .font(.headline) + .fontWeight(.medium) + .foregroundStyle(.secondary) + .fixedSize() + Spacer() } - // Expanded UI goes here. Compose the expanded UI through - // various regions, like leading/trailing/center/bottom - DynamicIslandExpandedRegion(.trailing, priority: 1) { - HStack(alignment: .lastTextBaseline) { - - Spacer() - TimerView(timerRange: context.state.timerRange) - .tint(Color("LightIndigo")) + DynamicIslandExpandedRegion(.center) { + VStack(alignment: .center, spacing: 0) { + BatteryIcon(batteryLevel: Int32(context.state.batteryLevel), font: .title, color: .accentColor) + Text(String(context.state.batteryLevel) + "%") + .font(.title3) + .foregroundColor(.gray) + .fixedSize() } - .padding(.top) + } + DynamicIslandExpandedRegion(.trailing, priority: 1) { + TimerView(timerRange: context.state.timerRange) + .tint(Color("LightIndigo")) } + DynamicIslandExpandedRegion(.bottom){ + Text(context.attributes.name) + .font(context.attributes.name.count > 14 ? .callout : .title3) + .fontWeight(.semibold) + .foregroundStyle(.tint) + Text("Last Heard: \(Date().formatted())") + .font(.caption) + .fontWeight(.medium) + .foregroundStyle(.secondary) + .fixedSize() + } } compactLeading: { Image("logo-black") @@ -65,7 +92,7 @@ struct WidgetsLiveActivity: Widget { @available(iOS 16.2, *) struct WidgetsLiveActivity_Previews: PreviewProvider { - static let attributes = MeshActivityAttributes(nodeNum: 123456789, name: "Meshtastic 8E6G") + static let attributes = MeshActivityAttributes(nodeNum: 123456789, name: "RAK Compact Rotary Handset Gray 8E6G") static let state = MeshActivityAttributes.ContentState( timerRange: Date.now...Date(timeIntervalSinceNow: 3600), connected: true, channelUtilization: 25.84, airtime: 10.01, batteryLevel: 39) @@ -134,34 +161,34 @@ struct NodeInfoView: View { .fontWeight(.semibold) .foregroundStyle(.tint) Text("\(String(format: "Ch. Util: %.2f", channelUtilization))%") - .font(.subheadline) + .font(.headline) .fontWeight(.medium) .foregroundStyle(.secondary) .opacity(isLuminanceReduced ? 0.8 : 1.0) .fixedSize() Text("\(String(format: "Airtime: %.2f", airtime))%") - .font(.subheadline) + .font(.headline) .fontWeight(.medium) .foregroundStyle(.secondary) .opacity(isLuminanceReduced ? 0.8 : 1.0) .fixedSize() if batteryLevel < 101 { Text("Battery Level: \(batteryLevel > 0 ? String(batteryLevel) : "< 1")%") - .font(.subheadline) + .font(.headline) .fontWeight(.medium) .foregroundStyle(.secondary) .opacity(isLuminanceReduced ? 0.8 : 1.0) .fixedSize() } else { Text("Plugged In") - .font(.subheadline) + .font(.headline) .fontWeight(.medium) .foregroundStyle(.secondary) .opacity(isLuminanceReduced ? 0.8 : 1.0) .fixedSize() } Text(Date().formatted()) - .font(.subheadline) + .font(.headline) .fontWeight(.medium) .foregroundStyle(.secondary) .opacity(isLuminanceReduced ? 0.8 : 1.0) @@ -177,18 +204,11 @@ struct TimerView: View { var body: some View { VStack(alignment: .center) { - Text("NEXT") + Text("NEXT UPDATE") .font(.caption) .fontWeight(.medium) .foregroundStyle(.secondary) .opacity(isLuminanceReduced ? 0.5 : 1.0) - .fixedSize() - Text("UPDATE") - .font(.caption) - .fontWeight(.medium) - .foregroundStyle(.secondary) - .opacity(isLuminanceReduced ? 0.5 : 1.0) - .fixedSize() Text(timerInterval: timerRange, countsDown: true) .monospacedDigit() .multilineTextAlignment(.center) @@ -215,7 +235,6 @@ struct ExpandedTrailingView: View { var body: some View { HStack(alignment: .lastTextBaseline) { - Spacer() TimerView(timerRange: timerInterval) }