diff --git a/Meshtastic/Enums/LoraConfigEnums.swift b/Meshtastic/Enums/LoraConfigEnums.swift index 53084e54..781661c6 100644 --- a/Meshtastic/Enums/LoraConfigEnums.swift +++ b/Meshtastic/Enums/LoraConfigEnums.swift @@ -28,7 +28,47 @@ enum RegionCodes: Int, CaseIterable, Identifiable { case my_919 = 17 case sg_923 = 18 case lora24 = 13 - + var topic: String { + switch self { + case .unset: + "UNSET" + case .us: + "US" + case .eu433: + "EU_433" + case .eu868: + "EU_868" + case .cn: + "CN" + case .jp: + "JP" + case .anz: + "ANZ" + case .kr: + "KR" + case .tw: + "TW" + case .ru: + "RU" + case .in: + "IN" + case .nz865: + "NZ_865" + case .th: + "TH" + case .ua433: + "UA_433" + case .ua868: + "UA_868" + case .my_433: + "MY_433" + case .my_919: + "MY_919" + case .sg_923: + "SG_923" + case .lora24: + "LORA_24" + } } var id: Int { self.rawValue } var description: String { switch self { diff --git a/Meshtastic/Views/Nodes/Helpers/Map/NodeMapSwiftUI.swift b/Meshtastic/Views/Nodes/Helpers/Map/NodeMapSwiftUI.swift index 3511cfdb..91ca6db4 100644 --- a/Meshtastic/Views/Nodes/Helpers/Map/NodeMapSwiftUI.swift +++ b/Meshtastic/Views/Nodes/Helpers/Map/NodeMapSwiftUI.swift @@ -10,7 +10,6 @@ import CoreLocation #if canImport(MapKit) import MapKit #endif -import WeatherKit @available(iOS 17.0, macOS 14.0, *) struct NodeMapSwiftUI: View { @@ -60,6 +59,15 @@ struct NodeMapSwiftUI: View { Map(position: $position, bounds: MapCameraBounds(minimumDistance: 1, maximumDistance: .infinity), scope: mapScope) { /// Node Color from node.num let nodeColor = UIColor(hex: UInt32(node.num)) + /// Convex Hull + if showConvexHull { + if lineCoords.count > 0 { + let hull = lineCoords.getConvexHull() + MapPolygon(coordinates: hull) + .stroke(Color(nodeColor.darker()), lineWidth: 3) + .foregroundStyle(Color(nodeColor).opacity(0.4)) + } + } /// Route Lines if showRouteLines { let gradient = LinearGradient( @@ -73,114 +81,96 @@ struct NodeMapSwiftUI: View { MapPolyline(coordinates: lineCoords) .stroke(gradient, style: dashed) } - /// Convex Hull - if showConvexHull { - if lineCoords.count > 0 { - let hull = lineCoords.getConvexHull() - MapPolygon(coordinates: hull) - .stroke(Color(nodeColor.darker()), lineWidth: 3) - .foregroundStyle(Color(nodeColor).opacity(0.4)) - } - } - - /// Waypoint Annotations - if waypoints.count > 0 && showWaypoints { - ForEach(Array(waypoints), id: \.id) { waypoint in - Annotation(waypoint.name ?? "?", coordinate: waypoint.coordinate) { - LazyVStack { - CircleText(text: String(UnicodeScalar(Int(waypoint.icon)) ?? "📍"), color: Color.orange, circleSize: 35) - .onTapGesture(coordinateSpace: .named("nodemap")) { location in - selectedWaypoint = (selectedWaypoint == waypoint ? nil : waypoint) - } - } - } - } - } + + + /// Node Annotations ForEach(positionArray, id: \.id) { position in let pf = PositionFlags(rawValue: Int(position.nodePosition?.metadata?.positionFlags ?? 771)) let headingDegrees = Angle.degrees(Double(position.heading)) - /// Reduced Precision Map Circle - if position.latest && 11...16 ~= position.precisionBits { - let pp = PositionPrecision(rawValue: Int(position.precisionBits)) - let radius : CLLocationDistance = pp?.precisionMeters ?? 0 - if radius > 0.0 { - MapCircle(center: position.coordinate, radius: radius) - .foregroundStyle(Color(nodeColor).opacity(0.25)) - .stroke(.white, lineWidth: 2) +// /// Reduced Precision Map Circle +// if position.latest && 11...16 ~= position.precisionBits { +// let pp = PositionPrecision(rawValue: Int(position.precisionBits)) +// let radius : CLLocationDistance = pp?.precisionMeters ?? 0 +// if radius > 0.0 { +// MapCircle(center: position.coordinate, radius: radius) +// .foregroundStyle(Color(nodeColor).opacity(0.25)) +// .stroke(.white, lineWidth: 2) +// } +// } +// Annotation(position.latest ? node.user?.shortName ?? "?": "", coordinate: position.coordinate) { +// LazyVStack { +// if position.latest { +// ZStack { +// Circle() +// .fill(Color(nodeColor.lighter()).opacity(0.4).shadow(.drop(color: Color(nodeColor).isLight() ? .black : .white, radius: 5))) +// .foregroundStyle(Color(nodeColor.lighter()).opacity(0.3)) +// .frame(width: 50, height: 50) +// if pf.contains(.Heading) { +// Image(systemName: pf.contains(.Speed) && position.speed > 1 ? "location.north" : "octagon") +// .symbolEffect(.pulse.byLayer) +// .padding(5) +// .foregroundStyle(Color(nodeColor).isLight() ? .black : .white) +// .background(Color(nodeColor.darker())) +// .clipShape(Circle()) +// .rotationEffect(headingDegrees) +// .onTapGesture { +// selectedPosition = (selectedPosition == position ? nil : position) +// } +// .popover(item: $selectedPosition) { selection in +// PositionPopover(position: selection) +// .padding() +// .opacity(0.8) +// .presentationCompactAdaptation(.popover) +// } +// +// } else { +// Image(systemName: "flipphone") +// .symbolEffect(.pulse.byLayer) +// .padding(5) +// .foregroundStyle(Color(nodeColor).isLight() ? .black : .white) +// .background(Color(UIColor(hex: UInt32(node.num)).darker())) +// .clipShape(Circle()) +// .onTapGesture { +// selectedPosition = (selectedPosition == position ? nil : position) +// } +// .popover(item: $selectedPosition) { selection in +// PositionPopover(position: selection) +// .padding() +// .opacity(0.8) +// .presentationCompactAdaptation(.popover) +// } +// +// } +// } +// } else { +// if showNodeHistory { +// if pf.contains(.Heading) { +// Image(systemName: "location.north.circle") +// .resizable() +// .scaledToFit() +// .foregroundStyle(Color(UIColor(hex: UInt32(node.num))).isLight() ? .black : .white) +// .background(Color(UIColor(hex: UInt32(node.num)))) +// .clipShape(Circle()) +// .rotationEffect(headingDegrees) +// .frame(width: 16, height: 16) +// +// } else { +// Circle() +// .fill(Color(UIColor(hex: UInt32(node.num)))) +// .strokeBorder(Color(UIColor(hex: UInt32(node.num))).isLight() ? .black : .white ,lineWidth: 2) +// .frame(width: 12, height: 12) +// } +// } +// } + + } - } - Annotation(position.latest ? node.user?.shortName ?? "?": "", coordinate: position.coordinate) { - LazyVStack { - if position.latest { - ZStack { - Circle() - .fill(Color(nodeColor.lighter()).opacity(0.4).shadow(.drop(color: Color(nodeColor).isLight() ? .black : .white, radius: 5))) - .foregroundStyle(Color(nodeColor.lighter()).opacity(0.3)) - .frame(width: 50, height: 50) - if pf.contains(.Heading) { - Image(systemName: pf.contains(.Speed) && position.speed > 1 ? "location.north" : "octagon") - .symbolEffect(.pulse.byLayer) - .padding(5) - .foregroundStyle(Color(nodeColor).isLight() ? .black : .white) - .background(Color(nodeColor.darker())) - .clipShape(Circle()) - .rotationEffect(headingDegrees) - .onTapGesture { - selectedPosition = (selectedPosition == position ? nil : position) - } - .popover(item: $selectedPosition) { selection in - PositionPopover(position: selection) - .padding() - .opacity(0.8) - .presentationCompactAdaptation(.popover) - } - - } else { - Image(systemName: "flipphone") - .symbolEffect(.pulse.byLayer) - .padding(5) - .foregroundStyle(Color(nodeColor).isLight() ? .black : .white) - .background(Color(UIColor(hex: UInt32(node.num)).darker())) - .clipShape(Circle()) - .onTapGesture { - selectedPosition = (selectedPosition == position ? nil : position) - } - .popover(item: $selectedPosition) { selection in - PositionPopover(position: selection) - .padding() - .opacity(0.8) - .presentationCompactAdaptation(.popover) - } - - } - } - } else { - if showNodeHistory { - if pf.contains(.Heading) { - Image(systemName: "location.north.circle") - .resizable() - .scaledToFit() - .foregroundStyle(Color(UIColor(hex: UInt32(node.num))).isLight() ? .black : .white) - .background(Color(UIColor(hex: UInt32(node.num)))) - .clipShape(Circle()) - .rotationEffect(headingDegrees) - .frame(width: 16, height: 16) - - } else { - Circle() - .fill(Color(UIColor(hex: UInt32(node.num)))) - .strokeBorder(Color(UIColor(hex: UInt32(node.num))).isLight() ? .black : .white ,lineWidth: 2) - .frame(width: 12, height: 12) - } - } - } - } - } - .tag(position.time) - .annotationTitles(.automatic) - .annotationSubtitles(.automatic) - } +// } +// .tag(position.time) +// .annotationTitles(.automatic) +// .annotationSubtitles(.automatic) +// } } .mapScope(mapScope) .mapStyle(mapStyle) diff --git a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift index 97a7225a..3d228ad3 100644 --- a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift @@ -26,7 +26,9 @@ struct MQTTConfig: View { @State var root = "msh" @State var selectedTopic = "" @State var mqttConnected: Bool = false + @State var defaultTopic = "msh/US" @State var nearbyTopics = [String]() + let locale = Locale.current var body: some View { VStack { @@ -315,6 +317,8 @@ struct MQTTConfig: View { nearbyTopics = [] let geocoder = CLGeocoder() if LocationsHandler.shared.locationsArray.count > 0 { + let region = RegionCodes(rawValue: Int(node?.loRaConfig?.regionCode ?? 0))?.topic + defaultTopic = "msh/" + (region ?? "UNSET") geocoder.reverseGeocodeLocation(LocationsHandler.shared.locationsArray.first!, completionHandler: {(placemarks, error) -> Void in if error != nil { print("Failed to reverse geocode location") @@ -322,27 +326,29 @@ struct MQTTConfig: View { } if let placemarks = placemarks, let placemark = placemarks.first { - + let cc = locale.region?.identifier ?? "UNK" /// Country Topic unless you are US - if placemark.isoCountryCode ?? "unknown" != "US" { - let countryTopic = root + "/" + (placemark.isoCountryCode ?? "") + if placemark.isoCountryCode ?? "unknown" != cc { + let countryTopic = defaultTopic + "/" + (placemark.isoCountryCode ?? "") if !countryTopic.isEmpty { nearbyTopics.append(countryTopic) } } - let stateTopic = root + "/" + (placemark.administrativeArea ?? "") + let stateTopic = defaultTopic + "/" + (placemark.administrativeArea ?? "") if !stateTopic.isEmpty { nearbyTopics.append(stateTopic) } - let countyTopic = root + "/" + (placemark.subAdministrativeArea?.lowercased().replacingOccurrences(of: " ", with: "") ?? "") + let countyTopic = defaultTopic + "/" + (placemark.subAdministrativeArea?.lowercased().replacingOccurrences(of: " ", with: "") ?? "") if !countyTopic.isEmpty { nearbyTopics.append(countyTopic) } - let cityTopic = root + "/" + (placemark.locality?.lowercased().replacingOccurrences(of: " ", with: "") ?? "") + let cityTopic = defaultTopic + "/" + (placemark.locality?.lowercased().replacingOccurrences(of: " ", with: "") ?? "") if !cityTopic.isEmpty { nearbyTopics.append(cityTopic) } - let neightborhoodTopic = root + "/" + (placemark.subLocality?.lowercased().replacingOccurrences(of: " ", with: "") ?? "") + let neightborhoodTopic = defaultTopic + "/" + (placemark.subLocality?.lowercased() + .replacingOccurrences(of: " ", with: "") + .replacingOccurrences(of: "'", with: "") ?? "") if !neightborhoodTopic.isEmpty { nearbyTopics.append(neightborhoodTopic) } diff --git a/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift b/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift index 48cb9b26..7483118b 100644 --- a/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift @@ -185,13 +185,13 @@ struct TelemetryConfig: View { } } func setTelemetryValues() { - self.deviceUpdateInterval = Int(node?.telemetryConfig?.deviceUpdateInterval ?? 0) - self.environmentUpdateInterval = Int(node?.telemetryConfig?.environmentUpdateInterval ?? 0) + self.deviceUpdateInterval = Int(node?.telemetryConfig?.deviceUpdateInterval ?? 900) + self.environmentUpdateInterval = Int(node?.telemetryConfig?.environmentUpdateInterval ?? 900) self.environmentMeasurementEnabled = node?.telemetryConfig?.environmentMeasurementEnabled ?? false self.environmentScreenEnabled = node?.telemetryConfig?.environmentScreenEnabled ?? false self.environmentDisplayFahrenheit = node?.telemetryConfig?.environmentDisplayFahrenheit ?? false self.powerMeasurementEnabled = node?.telemetryConfig?.powerMeasurementEnabled ?? false - self.powerUpdateInterval = Int(node?.telemetryConfig?.powerUpdateInterval ?? 0) + self.powerUpdateInterval = Int(node?.telemetryConfig?.powerUpdateInterval ?? 900) self.powerScreenEnabled = node?.telemetryConfig?.powerScreenEnabled ?? false self.hasChanges = false }