Kill the CarBode!

This commit is contained in:
Garth Vander Houwen 2022-04-19 22:56:59 -07:00
parent ada29b167d
commit be8577a610
9 changed files with 158 additions and 232 deletions

View file

@ -25,9 +25,7 @@
DD5394FC276993AD00AD86B1 /* SwiftProtobuf in Frameworks */ = {isa = PBXBuildFile; productRef = DD5394FB276993AD00AD86B1 /* SwiftProtobuf */; };
DD5394FE276BA0EF00AD86B1 /* PositionEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD5394FD276BA0EF00AD86B1 /* PositionEntityExtension.swift */; };
DD539502276DAA6A00AD86B1 /* MapLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD539501276DAA6A00AD86B1 /* MapLocation.swift */; };
DD6B85A62800915B000ACD6B /* CarBode in Frameworks */ = {isa = PBXBuildFile; productRef = DD6B85A52800915B000ACD6B /* CarBode */; };
DD6B85A828009258000ACD6B /* ShareChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6B85A728009258000ACD6B /* ShareChannel.swift */; };
DD6B85AA2800DFE5000ACD6B /* QRCamera.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6B85A92800DFE5000ACD6B /* QRCamera.swift */; };
DD8169F9271F1A6100F4AB02 /* MeshLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8169F8271F1A6100F4AB02 /* MeshLogger.swift */; };
DD8169FB271F1F3A00F4AB02 /* MeshLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */; };
DD8169FF272476C700F4AB02 /* LogDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8169FE272476C700F4AB02 /* LogDocument.swift */; };
@ -95,7 +93,6 @@
DD5394FD276BA0EF00AD86B1 /* PositionEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionEntityExtension.swift; sourceTree = "<group>"; };
DD539501276DAA6A00AD86B1 /* MapLocation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapLocation.swift; sourceTree = "<group>"; };
DD6B85A728009258000ACD6B /* ShareChannel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareChannel.swift; sourceTree = "<group>"; };
DD6B85A92800DFE5000ACD6B /* QRCamera.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCamera.swift; sourceTree = "<group>"; };
DD8169F8271F1A6100F4AB02 /* MeshLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshLogger.swift; sourceTree = "<group>"; };
DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshLog.swift; sourceTree = "<group>"; };
DD8169FE272476C700F4AB02 /* LogDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogDocument.swift; sourceTree = "<group>"; };
@ -140,7 +137,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
DD6B85A62800915B000ACD6B /* CarBode in Frameworks */,
C9697FA527933B8C00250207 /* SQLite in Frameworks */,
DD5394FC276993AD00AD86B1 /* SwiftProtobuf in Frameworks */,
);
@ -350,7 +346,6 @@
DD47E3D826F3093800029299 /* MessageBubble.swift */,
DD90860B26F684AF00DC5189 /* BatteryIcon.swift */,
DDF924C926FBB953009FE055 /* ConnectedDevice.swift */,
DD6B85A92800DFE5000ACD6B /* QRCamera.swift */,
);
path = Helpers;
sourceTree = "<group>";
@ -396,7 +391,6 @@
packageProductDependencies = (
DD5394FB276993AD00AD86B1 /* SwiftProtobuf */,
C9697FA427933B8C00250207 /* SQLite */,
DD6B85A52800915B000ACD6B /* CarBode */,
);
productName = MeshtasticClient;
productReference = DDC2E15426CE248E0042C5E4 /* MeshtasticClient.app */;
@ -473,7 +467,6 @@
packageReferences = (
DD5394FA276993AD00AD86B1 /* XCRemoteSwiftPackageReference "swift-protobuf" */,
C9697FA327933B8C00250207 /* XCRemoteSwiftPackageReference "SQLite.swift" */,
DD6B85A42800915B000ACD6B /* XCRemoteSwiftPackageReference "CarBode-Barcode-Scanner-For-SwiftUI" */,
);
productRefGroup = DDC2E15526CE248E0042C5E4 /* Products */;
projectDirPath = "";
@ -539,7 +532,6 @@
files = (
DD4DED9027AD2975004BA27E /* cannedmessages.pb.swift in Sources */,
DD836AE726F6B38600ABCC23 /* Connect.swift in Sources */,
DD6B85AA2800DFE5000ACD6B /* QRCamera.swift in Sources */,
DDAF8C6E26ED19040058C060 /* Extensions.swift in Sources */,
DDC2E1A726CEB3400042C5E4 /* LocationHelper.swift in Sources */,
DDAF8C5F26ED09B50058C060 /* radioconfig.pb.swift in Sources */,
@ -743,7 +735,7 @@
CODE_SIGN_ENTITLEMENTS = MeshtasticClient/MeshtasticClient.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 20;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_ASSET_PATHS = "\"MeshtasticClient/Preview Content\"";
DEVELOPMENT_TEAM = GCH7VS5Y9R;
ENABLE_PREVIEWS = YES;
@ -754,7 +746,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.2.53;
MARKETING_VERSION = 1.3.1;
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTS_MACCATALYST = YES;
@ -775,7 +767,7 @@
CODE_SIGN_ENTITLEMENTS = MeshtasticClient/MeshtasticClient.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 20;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_ASSET_PATHS = "\"MeshtasticClient/Preview Content\"";
DEVELOPMENT_TEAM = GCH7VS5Y9R;
ENABLE_PREVIEWS = YES;
@ -786,7 +778,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.2.53;
MARKETING_VERSION = 1.3.1;
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTS_MACCATALYST = YES;
@ -940,14 +932,6 @@
minimumVersion = 1.0.0;
};
};
DD6B85A42800915B000ACD6B /* XCRemoteSwiftPackageReference "CarBode-Barcode-Scanner-For-SwiftUI" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/heart/CarBode-Barcode-Scanner-For-SwiftUI";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 2.0.0;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
@ -961,11 +945,6 @@
package = DD5394FA276993AD00AD86B1 /* XCRemoteSwiftPackageReference "swift-protobuf" */;
productName = SwiftProtobuf;
};
DD6B85A52800915B000ACD6B /* CarBode */ = {
isa = XCSwiftPackageProductDependency;
package = DD6B85A42800915B000ACD6B /* XCRemoteSwiftPackageReference "CarBode-Barcode-Scanner-For-SwiftUI" */;
productName = CarBode;
};
/* End XCSwiftPackageProductDependency section */
/* Begin XCVersionGroup section */

View file

@ -1,22 +1,13 @@
{
"object": {
"pins": [
{
"package": "CarBode",
"repositoryURL": "https://github.com/heart/CarBode-Barcode-Scanner-For-SwiftUI",
"state": {
"branch": null,
"revision": "6167624ec47174900434f1c03dfa0b2dd5d19c0d",
"version": "2.2.3"
}
},
{
"package": "SQLite.swift",
"repositoryURL": "https://github.com/stephencelis/SQLite.swift.git",
"state": {
"branch": null,
"revision": "60a65015f6402b7c34b9a924f755ca0a73afeeaa",
"version": "0.13.1"
"revision": "4d543d811ee644fa4cc4bfa0be996b4dd6ba0f54",
"version": "0.13.3"
}
},
{
@ -24,8 +15,8 @@
"repositoryURL": "https://github.com/apple/swift-protobuf.git",
"state": {
"branch": null,
"revision": "7e2c5f3cbbeea68e004915e3a8961e20bd11d824",
"version": "1.18.0"
"revision": "e1499bc69b9040b29184f7f2996f7bab467c1639",
"version": "1.19.0"
}
}
]

View file

@ -523,11 +523,13 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
if decodedInfo.nodeInfo.hasDeviceMetrics {
newNode.batteryLevel = Int32(decodedInfo.nodeInfo.deviceMetrics.batteryLevel)
newNode.voltage = decodedInfo.nodeInfo.deviceMetrics.voltage
newNode.channelUtilization = decodedInfo.nodeInfo.deviceMetrics.channelUtilization
self.connectedPeripheral.channelUtilization = newNode.channelUtilization
newNode.airUtilTx = decodedInfo.nodeInfo.deviceMetrics.airUtilTx
let newTelemetry = TelemetryEntity(context: context!)
newTelemetry.batteryLevel = Int32(decodedInfo.nodeInfo.deviceMetrics.batteryLevel)
newTelemetry.voltage = decodedInfo.nodeInfo.deviceMetrics.voltage
newTelemetry.channelUtilization = decodedInfo.nodeInfo.deviceMetrics.channelUtilization
self.connectedPeripheral.channelUtilization = newTelemetry.channelUtilization
newTelemetry.airUtilTx = decodedInfo.nodeInfo.deviceMetrics.airUtilTx
self.connectedPeripheral.airTime = decodedInfo.nodeInfo.deviceMetrics.airUtilTx
}
@ -988,7 +990,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
if let telemetryMessage = try? Telemetry(serializedData: decodedInfo.packet.decoded.payload) {
let telemetry = TelemetryEntity(context: context!)
telemetry.num = Int64(decodedInfo.packet.from)
print(decodedInfo.packet.decoded.requestID)
print(telemetryMessage)
}
@ -1178,6 +1179,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
do {
let fetchedNode = try context?.fetch(fetchNode) as! [NodeInfoEntity]
if fetchedNode.count == 1 {
@ -1207,10 +1209,9 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
print("📍 Sent a Position Packet from the phone to the device for node: \(fromNodeNum)")
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
success = true
}
}

View file

@ -45,6 +45,8 @@
<string>We use bluetooth to connect to nearby Meshtastic Devices</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>Bluetooth is used to connect an iPhone to a user's meshtastic device to allow text messaging and location data for the mesh network.</string>
<key>NSCameraUsageDescription</key>
<string>We use the camera to share channels using a QR Code</string>
<key>NSLocationUsageDescription</key>
<string>We use your location to display it on the mesh map as well as to have GPS coordinatess to send to the connected device.</string>
<key>NSLocationWhenInUseUsageDescription</key>

View file

@ -24,6 +24,7 @@
<entity name="MyInfoEntity" representedClassName="MyInfoEntity" syncable="YES" codeGenerationType="class">
<attribute name="bitrate" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="bleName" optional="YES" attributeType="String"/>
<attribute name="errorCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="firmwareVersion" attributeType="String"/>
<attribute name="hasGps" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="hasWifi" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
@ -31,6 +32,7 @@
<attribute name="messageTimeoutMsec" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="minAppVersion" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="myNodeNum" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="rebootCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="myInfoNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="myInfo" inverseEntity="NodeInfoEntity"/>
<uniquenessConstraints>
<uniquenessConstraint>
@ -39,15 +41,11 @@
</uniquenessConstraints>
</entity>
<entity name="NodeInfoEntity" representedClassName="NodeInfoEntity" syncable="YES" codeGenerationType="class">
<attribute name="airUtilTx" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="batteryLevel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="bleName" optional="YES" attributeType="String"/>
<attribute name="channelUtilization" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="lastHeard" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="voltage" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<relationship name="myInfo" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="myInfoNode" inverseEntity="MyInfoEntity"/>
<relationship name="positions" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PositionEntity" inverseName="nodePosition" inverseEntity="PositionEntity"/>
<relationship name="telemetries" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TelemetryEntity" inverseName="nodeTelemetry" inverseEntity="TelemetryEntity"/>
@ -67,10 +65,12 @@
<relationship name="nodePosition" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positions" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="TelemetryEntity" representedClassName="TelemetryEntity" syncable="YES" codeGenerationType="class">
<attribute name="airUtilTx" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="barometricPressure" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="batteryLevel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="channelUtilization" optional="YES" attributeType="Float" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="current" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="gasResistance" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="num" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="relativeHumidity" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="temperature" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
@ -94,10 +94,10 @@
</entity>
<elements>
<element name="MessageEntity" positionX="-36" positionY="63" width="128" height="215"/>
<element name="MyInfoEntity" positionX="-18" positionY="81" width="128" height="179"/>
<element name="NodeInfoEntity" positionX="-63" positionY="-18" width="128" height="14"/>
<element name="MyInfoEntity" positionX="-18" positionY="81" width="128" height="209"/>
<element name="NodeInfoEntity" positionX="-63" positionY="-18" width="128" height="164"/>
<element name="PositionEntity" positionX="-54" positionY="54" width="128" height="119"/>
<element name="TelemetryEntity" positionX="160" positionY="192" width="128" height="14"/>
<element name="TelemetryEntity" positionX="160" positionY="192" width="128" height="194"/>
<element name="UserEntity" positionX="0" positionY="144" width="128" height="200"/>
</elements>
</model>

View file

@ -1171,9 +1171,27 @@ struct DataMessage {
/// Formerly named typ and of type Type
var portnum: PortNum = .unknownApp
var payloadVariant: DataMessage.OneOf_PayloadVariant? = nil
///
/// TODO: REPLACE
var payload: Data = Data()
var payload: Data {
get {
if case .payload(let v)? = payloadVariant {return v}
return Data()
}
set {payloadVariant = .payload(newValue)}
}
///
/// TODO: REPLACE
var payloadCompressed: Data {
get {
if case .payloadCompressed(let v)? = payloadVariant {return v}
return Data()
}
set {payloadVariant = .payloadCompressed(newValue)}
}
///
/// Not normally used, but for testing a sender can request that recipient
@ -1222,6 +1240,34 @@ struct DataMessage {
var unknownFields = SwiftProtobuf.UnknownStorage()
enum OneOf_PayloadVariant: Equatable {
///
/// TODO: REPLACE
case payload(Data)
///
/// TODO: REPLACE
case payloadCompressed(Data)
#if !swift(>=4.1)
static func ==(lhs: DataMessage.OneOf_PayloadVariant, rhs: DataMessage.OneOf_PayloadVariant) -> Bool {
// 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 (lhs, rhs) {
case (.payload, .payload): return {
guard case .payload(let l) = lhs, case .payload(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.payloadCompressed, .payloadCompressed): return {
guard case .payloadCompressed(let l) = lhs, case .payloadCompressed(let r) = rhs else { preconditionFailure() }
return l == r
}()
default: return false
}
}
#endif
}
init() {}
fileprivate var _location: Location? = nil
@ -2266,6 +2312,7 @@ extension Routing: @unchecked Sendable {}
extension Routing.OneOf_Variant: @unchecked Sendable {}
extension Routing.Error: @unchecked Sendable {}
extension DataMessage: @unchecked Sendable {}
extension DataMessage.OneOf_PayloadVariant: @unchecked Sendable {}
extension Location: @unchecked Sendable {}
extension MeshPacket: @unchecked Sendable {}
extension MeshPacket.OneOf_PayloadVariant: @unchecked Sendable {}
@ -2830,6 +2877,7 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "portnum"),
2: .same(proto: "payload"),
10: .standard(proto: "payload_compressed"),
3: .standard(proto: "want_response"),
4: .same(proto: "dest"),
5: .same(proto: "source"),
@ -2846,7 +2894,14 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularEnumField(value: &self.portnum) }()
case 2: try { try decoder.decodeSingularBytesField(value: &self.payload) }()
case 2: try {
var v: Data?
try decoder.decodeSingularBytesField(value: &v)
if let v = v {
if self.payloadVariant != nil {try decoder.handleConflictingOneOf()}
self.payloadVariant = .payload(v)
}
}()
case 3: try { try decoder.decodeSingularBoolField(value: &self.wantResponse) }()
case 4: try { try decoder.decodeSingularFixed32Field(value: &self.dest) }()
case 5: try { try decoder.decodeSingularFixed32Field(value: &self.source) }()
@ -2854,6 +2909,14 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
case 7: try { try decoder.decodeSingularFixed32Field(value: &self.replyID) }()
case 8: try { try decoder.decodeSingularFixed32Field(value: &self.emoji) }()
case 9: try { try decoder.decodeSingularMessageField(value: &self._location) }()
case 10: try {
var v: Data?
try decoder.decodeSingularBytesField(value: &v)
if let v = v {
if self.payloadVariant != nil {try decoder.handleConflictingOneOf()}
self.payloadVariant = .payloadCompressed(v)
}
}()
default: break
}
}
@ -2867,9 +2930,9 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
if self.portnum != .unknownApp {
try visitor.visitSingularEnumField(value: self.portnum, fieldNumber: 1)
}
if !self.payload.isEmpty {
try visitor.visitSingularBytesField(value: self.payload, fieldNumber: 2)
}
try { if case .payload(let v)? = self.payloadVariant {
try visitor.visitSingularBytesField(value: v, fieldNumber: 2)
} }()
if self.wantResponse != false {
try visitor.visitSingularBoolField(value: self.wantResponse, fieldNumber: 3)
}
@ -2891,12 +2954,15 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
try { if let v = self._location {
try visitor.visitSingularMessageField(value: v, fieldNumber: 9)
} }()
try { if case .payloadCompressed(let v)? = self.payloadVariant {
try visitor.visitSingularBytesField(value: v, fieldNumber: 10)
} }()
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: DataMessage, rhs: DataMessage) -> Bool {
if lhs.portnum != rhs.portnum {return false}
if lhs.payload != rhs.payload {return false}
if lhs.payloadVariant != rhs.payloadVariant {return false}
if lhs.wantResponse != rhs.wantResponse {return false}
if lhs.dest != rhs.dest {return false}
if lhs.source != rhs.source {return false}

View file

@ -1,133 +0,0 @@
//
// QRCamera.swift
// MeshtasticClient
//
// Created by Garth Vander Houwen on 4/8/22.
//
import SwiftUI
import CarBode
import AVFoundation
struct cameraFrame: Shape {
func path(in rect: CGRect) -> Path {
Path { path in
let width = rect.width
let height = rect.height
path.addLines( [
CGPoint(x: 0, y: height * 0.25),
CGPoint(x: 0, y: 0),
CGPoint(x:width * 0.25, y:0)
])
path.addLines( [
CGPoint(x: width * 0.75, y: 0),
CGPoint(x: width, y: 0),
CGPoint(x:width, y:height * 0.25)
])
path.addLines( [
CGPoint(x: width, y: height * 0.75),
CGPoint(x: width, y: height),
CGPoint(x:width * 0.75, y: height)
])
path.addLines( [
CGPoint(x:width * 0.25, y: height),
CGPoint(x:0, y: height),
CGPoint(x:0, y:height * 0.75)
])
}
}
}
struct QRCamera: View {
@State var barcodeValue = ""
@State var torchIsOn = false
@State var showingAlert = false
@State var cameraPosition = AVCaptureDevice.Position.back
var body: some View {
VStack {
Text("QRCode Scanner")
Spacer()
if cameraPosition == .back{
Text("Using back camera")
}else{
Text("Using front camera")
}
Button(action: {
if cameraPosition == .back {
cameraPosition = .front
}else{
cameraPosition = .back
}
}) {
if cameraPosition == .back{
Text("Switch Camera to Front")
}else{
Text("Switch Camera to Back")
}
}
Button(action: {
self.torchIsOn.toggle()
}) {
Text("Toggle Torch Light")
}
Spacer()
CBScanner(
supportBarcode: .constant([.qr, .code128]),
torchLightIsOn: $torchIsOn,
cameraPosition: $cameraPosition,
mockBarCode: .constant(BarcodeData(value:"My Test Data", type: .qr))
){
print("BarCodeType =",$0.type.rawValue, "Value =",$0.value)
barcodeValue = $0.value
}
onDraw: {
print("Preview View Size = \($0.cameraPreviewView.bounds)")
print("Barcode Corners = \($0.corners)")
let lineColor = UIColor.green
let fillColor = UIColor(red: 0, green: 1, blue: 0.2, alpha: 0.4)
//Draw Barcode corner
$0.draw(lineWidth: 1, lineColor: lineColor, fillColor: fillColor)
}.frame(minWidth: 0, maxWidth: .infinity, minHeight: 400, maxHeight: 400, alignment: .topLeading)
.overlay(cameraFrame()
.stroke(lineWidth: 5)
.frame(width: 500, height: 250)
.foregroundColor(.blue))
Spacer()
Text(barcodeValue)
Spacer()
}.alert(isPresented: $showingAlert) {
Alert(title: Text("Found Barcode"), message: Text("\(barcodeValue)"), dismissButton: .default(Text("Close")))
}
}
}
struct ModalScannerView_Previews: PreviewProvider {
static var previews: some View {
QRCamera()
}
}

View file

@ -104,7 +104,8 @@ struct NodeDetail: View {
}
}
.padding(5)
if node.snr > 0 {
Divider()
VStack(alignment: .center) {

View file

@ -6,69 +6,88 @@
//
import SwiftUI
import CoreData
import CarBode
import CoreImage.CIFilterBuiltins
struct QrCodeImage {
let context = CIContext()
func generateQRCode(from text: String) -> UIImage {
var qrImage = UIImage(systemName: "xmark.circle") ?? UIImage()
let data = Data(text.utf8)
let filter = CIFilter.qrCodeGenerator()
filter.setValue(data, forKey: "inputMessage")
let transform = CGAffineTransform(scaleX: 15, y: 15)
if let outputImage = filter.outputImage?.transformed(by: transform) {
if let image = context.createCGImage(
outputImage,
from: outputImage.extent) {
qrImage = UIImage(cgImage: image)
}
}
return qrImage
}
}
struct ShareChannel: View {
@Environment(\.managedObjectContext) var context
@EnvironmentObject var bleManager: BLEManager
@EnvironmentObject var userSettings: UserSettings
@State var dataString = "Hello Carbode"
@State var barcodeType = CBBarcodeView.BarcodeType.qrCode
@State var rotate = CBBarcodeView.Orientation.up
@State var barcodeImage: UIImage?
@State private var text = "meshtastic.org"
var qrCodeImage = QrCodeImage()
var body: some View {
HStack {
VStack {
GeometryReader { bounds in
let smallest = min(bounds.size.width, bounds.size.height)
ScrollView {
VStack {
let smallest = min(bounds.size.width, bounds.size.height)
Text("Channel Name").font(.largeTitle)
CBBarcodeView(data: $dataString,
barcodeType: $barcodeType,
orientation: $rotate)
{ image in
self.barcodeImage = image
}.frame(
VStack {
Text("Scan the QR code below with the Apple or Android device you would like to share with your channel settings with.")
.fixedSize(horizontal: false, vertical: true)
.font(.callout)
.padding()
Spacer()
let image = qrCodeImage.generateQRCode(from: text)
Image(uiImage: image)
.resizable()
.scaledToFit()
.frame(
minWidth: smallest * 0.9,
maxWidth: smallest * 0.9,
minHeight: smallest * 0.9,
maxHeight: smallest * 0.9,
alignment: .topLeading
alignment: .center
)
.padding(.bottom)
Text("Channel Details").font(.title)
Text("Some helpful text about how this whole thing works goes here, also could add a share sheet icon and pass the link around.")
Spacer()
Text("Some helpful text about how this whole thing works goes here, also could add a share sheet icon and pass the link around.")
Text("Channel Name (Long/Slow)").font(.title)
Spacer()
}
.frame(width: bounds.size.width, height: bounds.size.height)
}
}.padding()
}
.navigationTitle("Share Channel")
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(trailing:
}
.navigationTitle("Share Channel")
.navigationBarTitleDisplayMode(.automatic)
.navigationBarItems(trailing:
ZStack {
ZStack {
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "???")
})
.onAppear {
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "???")
})
.onAppear {
self.bleManager.context = context
self.bleManager.context = context
}
}
.navigationViewStyle(StackNavigationViewStyle())
}