Node Colors!

This commit is contained in:
Garth Vander Houwen 2023-03-31 12:08:42 -07:00
parent 64b3d42f2d
commit 2e416020e0
10 changed files with 136 additions and 74 deletions

View file

@ -20,6 +20,22 @@ extension CLLocationCoordinate2D {
}
}
extension Color {
func isLight() -> Bool {
guard let components = cgColor?.components, components.count > 2 else {return false}
let brightness = ((components[0] * 299) + (components[1] * 587) + (components[2] * 114)) / 1000
return (brightness > 0.5)
}
}
extension UIColor {
func isLight() -> Bool {
guard let components = cgColor.components, components.count > 2 else {return false}
let brightness = ((components[0] * 299) + (components[1] * 587) + (components[2] * 114)) / 1000
return (brightness > 0.5)
}
}
extension Data {
var macAddressString: String {
let mac: String = reduce("") {$0 + String(format: "%02x:", $1)}
@ -72,6 +88,21 @@ extension Int {
}
}
extension Int64 {
func uiColor() -> UIColor {
let color = UIColor(
red: CGFloat((self & 0xFF0000) >> 16) / 255.0,
green: CGFloat((self & 0x00FF00) >> 8) / 255.0,
blue: CGFloat(self & 0x0000FF) / 255.0,
alpha: CGFloat(1.0))
return color
}
}
extension UIImage {
func rotate(radians: Float) -> UIImage? {
var newSize = CGRect(origin: CGPoint.zero, size: self.size).applying(CGAffineTransform(rotationAngle: CGFloat(radians))).size

View file

@ -766,23 +766,25 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM
guard let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as? [MyInfoEntity] else {
return
}
for channel in (fetchedMyInfo[0].channels?.array ?? []) as? [ChannelEntity] ?? [] {
if channel.index == newMessage.channel {
context.refresh(channel, mergeChanges: true)
}
if channel.index == newMessage.channel && !channel.mute {
// Create an iOS Notification for the received private channel message and schedule it immediately
let manager = LocalNotificationManager()
manager.notifications = [
Notification(
id: ("notification.id.\(newMessage.messageId)"),
title: "\(newMessage.fromUser?.longName ?? NSLocalizedString("unknown", comment: "Unknown"))",
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "???")",
content: messageText)
]
manager.schedule()
print("💬 iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? NSLocalizedString("unknown", comment: "Unknown"))")
if !fetchedMyInfo.isEmpty {
for channel in (fetchedMyInfo[0].channels?.array ?? []) as? [ChannelEntity] ?? [] {
if channel.index == newMessage.channel {
context.refresh(channel, mergeChanges: true)
}
if channel.index == newMessage.channel && !channel.mute {
// Create an iOS Notification for the received private channel message and schedule it immediately
let manager = LocalNotificationManager()
manager.notifications = [
Notification(
id: ("notification.id.\(newMessage.messageId)"),
title: "\(newMessage.fromUser?.longName ?? NSLocalizedString("unknown", comment: "Unknown"))",
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "???")",
content: messageText)
]
manager.schedule()
print("💬 iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? NSLocalizedString("unknown", comment: "Unknown"))")
}
}
}
} catch {

View file

@ -11,6 +11,7 @@ struct CircleText: View {
var circleSize: CGFloat? = 60
var fontSize: CGFloat? = 20
var brightness: Double? = 0
var textColor: Color? = .white
var body: some View {
@ -21,7 +22,7 @@ struct CircleText: View {
.fill(color)
.brightness(brightness ?? 0)
.frame(width: circleSize, height: circleSize)
Text(text).textCase(.uppercase).font(font).foregroundColor(.white).fixedSize()
Text(text).textCase(.uppercase).font(font).foregroundColor(textColor).fixedSize()
.frame(width: circleSize, height: circleSize, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/).offset(x: 0, y: 0)
}
}

View file

@ -16,8 +16,6 @@ struct MapViewSwiftUI: UIViewRepresentable {
var onLongPress: (_ waypointCoordinate: CLLocationCoordinate2D) -> Void
var onWaypointEdit: (_ waypointId: Int ) -> Void
let mapView = MKMapView()
let lineColors: [UIColor] = [UIColor.systemIndigo, UIColor.yellow, UIColor.white, UIColor.red, UIColor.purple, UIColor.orange, UIColor.magenta, UIColor.lightGray, UIColor.green, UIColor.gray, UIColor.systemMint, UIColor.darkGray, UIColor.cyan, UIColor.brown, UIColor.blue, UIColor.black, UIColor.systemPink,
UIColor.systemTeal]
// Parameters
let positions: [PositionEntity]
let waypoints: [WaypointEntity]
@ -142,7 +140,7 @@ struct MapViewSwiftUI: UIViewRepresentable {
return position.nodeCoordinate!
})
let polyline = MKPolyline(coordinates: lineCoords, count: nodePositions.count)
polyline.title = "\(String(position.nodePosition?.num ?? 0))-\(String(lineIndex))"
polyline.title = "\(String(position.nodePosition?.num ?? 0))"
mapView.addOverlay(polyline)
lineIndex += 1
// There are 18 colors for lines, start over if we are at index 17
@ -199,7 +197,7 @@ struct MapViewSwiftUI: UIViewRepresentable {
annotationView.displayPriority = .required
annotationView.titleVisibility = .visible
} else {
annotationView.markerTintColor = UIColor(.indigo)
annotationView.markerTintColor = positionAnnotation.nodePosition?.num.uiColor()
annotationView.displayPriority = .defaultHigh
annotationView.titleVisibility = .adaptive
}
@ -351,10 +349,10 @@ struct MapViewSwiftUI: UIViewRepresentable {
} else {
if let routePolyline = overlay as? MKPolyline {
let titleString = routePolyline.title ?? "None-0"
let titleString = routePolyline.title ?? "0"
let index = Int(titleString.components(separatedBy: "-").last ?? "0")
let renderer = MKPolylineRenderer(polyline: routePolyline)
renderer.strokeColor = parent.lineColors[index ?? 0]
renderer.strokeColor = Int64(titleString)?.uiColor()
renderer.lineWidth = 8
return renderer
}

View file

@ -150,7 +150,7 @@ struct Contacts: View {
HStack {
VStack {
HStack {
CircleText(text: user.shortName ?? "???", color: .accentColor, circleSize: 52, fontSize: 16, brightness: 0.1)
CircleText(text: user.shortName ?? "???", color: Color(user.num.uiColor()), circleSize: 52, fontSize: 16, textColor: user.num.uiColor().isLight() ? .black : .white)
.padding(.trailing, 5)
VStack {
HStack {

View file

@ -21,25 +21,67 @@ struct DeviceMetricsLog: View {
let oneDayAgo = Calendar.current.date(byAdding: .day, value: -3, to: Date())
let deviceMetrics = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0")).reversed() as? [TelemetryEntity] ?? []
let batteryChartData = deviceMetrics
let chartData = deviceMetrics
.filter { $0.time != nil && $0.time! >= oneDayAgo! }
.sorted { $0.time! < $1.time! }
NavigationStack {
if batteryChartData.count > 0 {
if chartData.count > 0 {
GroupBox(label: Label("battery.level.trend", systemImage: "battery.100")) {
Chart(batteryChartData, id: \.self) {
LineMark(
x: .value("Hour", $0.time!.formattedDate(format: "ha")),
y: .value("Value", $0.batteryLevel)
)
Chart(chartData, id: \.self) {
PointMark(
x: .value("Hour", $0.time!.formattedDate(format: "ha")),
x: .value("Time", $0.time!, unit: .hour),
y: .value("Value", $0.channelUtilization)
)
.foregroundStyle(.green)
LineMark(
x: .value("Time", $0.time!, unit: .hour),
y: .value("Value", $0.channelUtilization)
)
.foregroundStyle(.green)
.interpolationMethod(.catmullRom)
PointMark(
x: .value("Time", $0.time!, unit: .hour),
y: .value("Value", $0.batteryLevel)
)
.foregroundStyle(.blue)
LineMark(
x: .value("Time", $0.time!, unit: .hour),
y: .value("Value", $0.batteryLevel)
)
.foregroundStyle(.blue)
PointMark(
//x: .value("Hour", $0.time!.formattedDate(format: "ha")),
x: .value("Time", $0.time!, unit: .hour),
y: .value("Value", $0.airUtilTx)
)
.foregroundStyle(.red)
LineMark(
x: .value("Time", $0.time!, unit: .hour),
y: .value("Value", $0.airUtilTx)
)
.foregroundStyle(.red)
}
.frame(height: 150)
// Set color for each data in the chart
.chartForegroundStyleScale([
"Battery Level" : .blue,
"Channel Utilization": .green,
"Airtime": .red
])
.chartLegend(position: .automatic, alignment: .bottom)
.chartXAxis {
AxisMarks(values: .stride(by: .hour))
}
//.frame(height: 200)
}
}
let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddjmma", options: 0, locale: Locale.current)

View file

@ -19,47 +19,36 @@ struct EnvironmentMetricsLog: View {
var node: NodeInfoEntity
var body: some View {
let environmentMetrics = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 1")).reversed() as? [TelemetryEntity] ?? []
NavigationStack {
let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddjmma", options: 0, locale: Locale.current)
let dateFormatString = (localeDateFormat ?? "MM/dd/YY j:mma").replacingOccurrences(of: ",", with: "")
Text("\(environmentMetrics.count) Readings")
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
// Add a table for mac and ipad
Table(node.telemetries!.reversed() as! [TelemetryEntity]) {
Table(environmentMetrics) {
TableColumn("Temperature") { em in
if em.metricsType == 1 {
Text(em.temperature.formattedTemperature())
}
Text(em.temperature.formattedTemperature())
}
TableColumn("Humidity") { em in
if em.metricsType == 1 {
Text("\(String(format: "%.2f", em.relativeHumidity))%")
}
Text("\(String(format: "%.2f", em.relativeHumidity))%")
}
TableColumn("Barometric Pressure") { em in
if em.metricsType == 1 {
Text("\(String(format: "%.2f", em.barometricPressure)) hPa")
}
Text("\(String(format: "%.2f", em.barometricPressure)) hPa")
}
TableColumn("gas.resistance") { em in
if em.metricsType == 1 {
Text("\(String(format: "%.2f", em.gasResistance)) ohms")
}
Text("\(String(format: "%.2f", em.gasResistance)) ohms")
}
TableColumn("current") { em in
if em.metricsType == 1 {
Text("\(String(format: "%.2f", em.current))")
}
Text("\(String(format: "%.2f", em.current))")
}
TableColumn("voltage") { em in
if em.metricsType == 1 {
Text("\(String(format: "%.2f", em.voltage))")
}
Text("\(String(format: "%.2f", em.voltage))")
}
TableColumn("timestamp") { em in
if em.metricsType == 1 {
Text(em.time?.formattedDate(format: dateFormatString) ?? NSLocalizedString("unknown.age", comment: ""))
}
Text(em.time?.formattedDate(format: dateFormatString) ?? NSLocalizedString("unknown.age", comment: ""))
}
}
} else {
@ -92,21 +81,18 @@ struct EnvironmentMetricsLog: View {
}
ForEach(node.telemetries?.reversed() as? [TelemetryEntity] ?? [], id: \.self) { (em: TelemetryEntity) in
if em.metricsType == 1 {
GridRow {
GridRow {
Text(em.temperature.formattedTemperature())
.font(.caption)
Text("\(String(format: "%.2f", em.relativeHumidity))%")
.font(.caption)
Text("\(String(format: "%.2f", em.barometricPressure))")
.font(.caption)
Text("\(String(format: "%.2f", em.gasResistance))")
.font(.caption)
Text(em.time?.formattedDate(format: dateFormatString) ?? NSLocalizedString("unknown.age", comment: ""))
.font(.caption2)
}
Text(em.temperature.formattedTemperature())
.font(.caption)
Text("\(String(format: "%.2f", em.relativeHumidity))%")
.font(.caption)
Text("\(String(format: "%.2f", em.barometricPressure))")
.font(.caption)
Text("\(String(format: "%.2f", em.gasResistance))")
.font(.caption)
Text(em.time?.formattedDate(format: dateFormatString) ?? NSLocalizedString("unknown.age", comment: ""))
.font(.caption2)
}
}
}

View file

@ -145,7 +145,7 @@ struct NodeDetail: View {
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
HStack {
VStack(alignment: .center) {
CircleText(text: node.user?.shortName ?? "???", color: .accentColor, circleSize: 75, fontSize: 26)
CircleText(text: node.user?.shortName ?? "???", color: Color(node.num.uiColor()), circleSize: 75, fontSize: 26, textColor: node.num.uiColor().isLight() ? .black : .white )
}
Divider()
VStack {

View file

@ -26,9 +26,11 @@ struct NodeList: View {
@State private var selection: NodeInfoEntity? // Nothing selected by default.
var body: some View {
NavigationSplitView {
List(nodes, id: \.self, selection: $selection) { node in
List(nodes, id: \.self, selection: $selection) { node in
if nodes.count == 0 {
Text("no.nodes").font(.title)
} else {
@ -36,7 +38,7 @@ struct NodeList: View {
let connected: Bool = (bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral?.num ?? -1 == node.num)
VStack(alignment: .leading) {
HStack {
CircleText(text: node.user?.shortName ?? "???", color: .accentColor, circleSize: 52, fontSize: 16, brightness: 0.1)
CircleText(text: node.user?.shortName ?? "???", color: Color(node.num.uiColor()), circleSize: 52, fontSize: 16, brightness: 0.0, textColor: node.num.uiColor().isLight() ? .black : .white)
.padding(.trailing, 5)
VStack(alignment: .leading) {
Text(node.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown")).font(.headline)

View file

@ -72,7 +72,7 @@ struct RangeTestConfig: View {
.font(.caption)
}
}
.disabled(self.bleManager.connectedPeripheral == nil || node?.positionConfig == nil || !(node != nil && node?.metadata?.hasWifi ?? false))
.disabled(self.bleManager.connectedPeripheral == nil || node?.rangeTestConfig == nil || !(node != nil && node?.metadata?.hasWifi ?? false))
Button {
isPresentingSaveConfirm = true
} label: {