         }
 
         self.txBuffer = encryptionManager.encrypt(requestMsg)
-        self.sendRequestMessage()
-    }
-
-    private func sendRequestMessage() {
+    }
+
+    private func sendRequestMessage(onReply: @escaping (ReplyMessage?) -> ()) {
+        self.onReplyCallback = onReply
+
         self.bluetoothConnection.send(data: txBuffer)
         DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + MeshSetup.bluetoothSendTimeoutValue,
                 execute: sendTimeoutWorker)
-
     }
 
     private func messageSendTimeout() {
-        log("Transceiver did timeout")
-        if (retryCount < MeshSetup.bluetoothSendTimeoutRetryCount) {
-            log("Retrying")
-            sendRequestMessage()
-        } else {
-            log("Delegate.didTimeOut")
-            self.delegate.didTimeoutSendingMessage(sender: self)
-        }
+        self.onReplyCallback(nil)
     }
 
     private func serialize(message: SwiftProtobuf.Message) -> Data {

        rxBuffer.removeAll()
        self.waitingForReply = false

        log("Received reply message id \(rm.id) --> Payload size: \(rm.data.count)")
        let replyRequestType = self.replyRequestTypeDict[rm.id]!

        switch replyRequestType {
            case .Auth:
                log("Received reply: \(replyRequestType)")
                self.delegate.didReceiveAuthReply(sender: self, result: rm.result)
            case .GetDeviceId:
                let decodedReply = try! Particle_Ctrl_GetDeviceIdReply(serializedData: rm.data) as! Particle_Ctrl_GetDeviceIdReply
                log("Received reply: \(replyRequestType)")
                self.delegate.didReceiveDeviceIdReply(sender: self, result:rm.result, deviceId: decodedReply.id)
            case .SetClaimCode:
                log("Received reply: \(replyRequestType)")
                self.delegate.didReceiveSetClaimCodeReply(sender: self, result: rm.result)
            case .GetSerialNumber:
                let decodedReply = try! Particle_Ctrl_GetSerialNumberReply(serializedData: rm.data) as! Particle_Ctrl_GetSerialNumberReply
                log("Received reply: \(replyRequestType)")
                self.delegate.didReceiveGetSerialNumberReply(sender: self, result: rm.result, serialNumber: decodedReply.serial)
            case .GetConnectionStatus:
                let decodedReply = try! Particle_Ctrl_Cloud_GetConnectionStatusReply(serializedData: rm.data) as! Particle_Ctrl_Cloud_GetConnectionStatusReply
                log("Received reply: \(replyRequestType)")
                self.delegate.didReceiveGetConnectionStatusReply(sender: self, result: rm.result, connectionStatus: decodedReply.status)
            case .IsClaimed:
                let decodedReply = try! Particle_Ctrl_IsClaimedReply(serializedData: rm.data) as! Particle_Ctrl_IsClaimedReply
                log("Received reply: \(replyRequestType)")
                self.delegate.didReceiveIsClaimedReply(sender: self, result: rm.result, isClaimed: decodedReply.claimed)
            case .CreateNetwork:
                let decodedReply = try! Particle_Ctrl_Mesh_CreateNetworkReply(serializedData: rm.data) as! Particle_Ctrl_Mesh_CreateNetworkReply
                log("Received reply: \(replyRequestType)")
                log("NetworkInfo:\nName: \(decodedReply.network.name)\nXPAN ID: \(decodedReply.network.extPanID)\nPAN ID: \(decodedReply.network.panID)\nChannel: \(decodedReply.network.channel)")
                self.delegate.didReceiveCreateNetworkReply(sender: self, result: rm.result, networkInfo: decodedReply.network)
            case .StartCommissioner:
                log("Received reply: \(replyRequestType)")
                self.delegate.didReceiveStartCommissionerReply(sender: self, result: rm.result)
            case .StopCommissioner:
                log("Received reply: \(replyRequestType)")
                self.delegate.didReceiveStopCommissionerReply(sender: self, result: rm.result)
            case .StartListening:
                log("Received reply: \(replyRequestType)")
                self.delegate.didReceiveStartListeningReply(sender: self, result: rm.result)
            case .StopListening:
                log("Received reply: \(replyRequestType)")
                self.delegate.didReceiveStopListeningReply(sender: self, result: rm.result)
            case .DeviceSetupDone:
                log("Received reply: \(replyRequestType)")
                self.delegate.didReceiveDeviceSetupDoneReply(sender: self, result: rm.result)
            case .IsDeviceSetupDone:
                let decodedReply = try! Particle_Ctrl_IsDeviceSetupDoneReply(serializedData: rm.data) as! Particle_Ctrl_IsDeviceSetupDoneReply
                log("Received reply: \(replyRequestType)")
                self.delegate.didReceiveIsDeviceSetupDoneReply(sender: self, result: rm.result, isDone: decodedReply.done)
            case .PrepareJoiner:
                let decodedReply = try! Particle_Ctrl_Mesh_PrepareJoinerReply(serializedData: rm.data) as! Particle_Ctrl_Mesh_PrepareJoinerReply
                log("Received reply: \(replyRequestType)")
                self.delegate.didReceivePrepareJoinerReply(sender: self, result: rm.result, eui64: decodedReply.eui64, password: decodedReply.password)
            case .AddJoiner:
                log("Received reply: \(replyRequestType)")
                self.delegate.didReceiveAddJoinerReply(sender: self, result: rm.result)
            case .RemoveJoiner:
                log("Received reply: \(replyRequestType)")
                self.delegate.didReceiveRemoveJoinerReply(sender: self, result: rm.result)
            case .JoinNetwork:
                log("Received reply: \(replyRequestType)")
                self.delegate.didReceiveJoinNetworkReply(sender: self, result: rm.result)
            case .LeaveNetwork:
                log("Received reply: \(replyRequestType)")
                self.delegate.didReceiveLeaveNetworkReply(sender: self, result: rm.result)
            case .GetNetworkInfo:
                let decodedReply = try! Particle_Ctrl_Mesh_GetNetworkInfoReply(serializedData: rm.data) as! Particle_Ctrl_Mesh_GetNetworkInfoReply
                log("Received reply: \(replyRequestType)")
                log("NetworkInfo:\nName: \(decodedReply.network.name)\nXPAN ID: \(decodedReply.network.extPanID)\nPAN ID: \(decodedReply.network.panID)\nChannel: \(decodedReply.network.channel)")
                self.delegate.didReceiveGetNetworkInfoReply(sender: self, result: rm.result, networkInfo: decodedReply.network)
            case .ScanNetworks:
                let decodedReply = try! Particle_Ctrl_Mesh_ScanNetworksReply(serializedData: rm.data) as! Particle_Ctrl_Mesh_ScanNetworksReply
                log("Received reply: \(replyRequestType)")
                self.delegate.didReceiveScanNetworksReply(sender: self, result: rm.result, networks: decodedReply.networks)
            case .GetInterfaceList:
                let decodedReply = try! Particle_Ctrl_GetInterfaceListReply(serializedData: rm.data) as! Particle_Ctrl_GetInterfaceListReply
                log("Received reply: \(replyRequestType)")
                self.delegate.didReceiveGetInterfaceListReply(sender: self, result: rm.result, networks: decodedReply.interfaces)

        }
    }
}


typealias MeshSetupNetworkInfo = Particle_Ctrl_Mesh_NetworkInfo
typealias CloudConnectionStatus = Particle_Ctrl_Cloud_ConnectionStatus
typealias MeshSetupNetworkInterface = Particle_Ctrl_InterfaceEntry

class MeshSetupProtocolTransceiver: NSObject, MeshSetupBluetoothConnectionDataDelegate {

    private var bluetoothConnection: MeshSetupBluetoothConnection
    private var encryptionManager: MeshSetupEncryptionManager

    private var requestMessageId: UInt16 = 1

    private var waitingForReply: Bool = false
    private var rxBuffer: Data = Data()
    private var txBuffer: Data!

    private var onReplyCallback: ((ReplyMessage?) -> ())!

    private lazy var sendTimeoutWorker: DispatchWorkItem  = DispatchWorkItem() {
        [weak self] in

        NSLog("what is happening?")

        if let sSelf = self {
            sSelf.messageSendTimeout()
        }

    }


    
    required init(connection: MeshSetupBluetoothConnection) {
        self.bluetoothConnection = connection
        self.encryptionManager = MeshSetupEncryptionManager(derivedSecret: bluetoothConnection.derivedSecret!)

