Connect to the KNX/IP tunnelling interface.

        If the remote address is "0.0.0.0", it will use the Gateway scanner
        to automatically detect a KNX gateway and it will connect to it if one
        has been found.

        Returns true if a connection could be established, false otherwise

def connect(self, timeout=2):
        """Connect to the KNX/IP tunnelling interface.

        If the remote address is "0.0.0.0", it will use the Gateway scanner
        to automatically detect a KNX gateway and it will connect to it if one
        has been found.

        Returns true if a connection could be established, false otherwise
        """

        if self.connected:
            logging.info("KNXIPTunnel connect request ignored, "
                         "already connected")
            return True

        if self.remote_ip == "0.0.0.0":
            scanner = GatewayScanner()
            try:
                ipaddr, port = scanner.start_search()
                logging.info("Found KNX gateway %s/%s", ipaddr, port)
                self.remote_ip = ipaddr
                self.remote_port = port
            except TypeError:
                logging.error("No KNX/IP gateway given and no gateway "
                              "found by scanner, aborting %s")

        # Clean up cache
        self.value_cache.clear()

        # Find my own IP
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.connect((self.remote_ip, self.remote_port))
        local_ip = sock.getsockname()[0]

        if self.data_server:
            logging.info("Data server already running, not starting again")
        else:
            self.data_server = DataServer((local_ip, 0),
                                          DataRequestHandler,
                                          self)
            dummy_ip, self.data_port = self.data_server.server_address
            data_server_thread = threading.Thread(
                target=self.data_server.serve_forever)
            data_server_thread.daemon = True
            data_server_thread.start()
            logging.debug(
                "Started data server on UDP port %s", self.data_port)

        self.control_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.control_socket.bind((local_ip, 0))
        self.control_socket.settimeout(timeout)

        # Connect packet
        frame = KNXIPFrame(KNXIPFrame.CONNECT_REQUEST)

        # Control endpoint
        body = []
        body.extend([0x08, 0x01])  # length 8 bytes, UPD
        dummy_ip, port = self.control_socket.getsockname()
        body.extend(ip_to_array(local_ip))
        body.extend(int_to_array(port, 2))

        # Data endpoint
        body.extend([0x08, 0x01])  # length 8 bytes, UPD
        body.extend(ip_to_array(local_ip))
        body.extend(int_to_array(self.data_port, 2))

        #
        body.extend([0x04, 0x04, 0x02, 0x00])
        frame.body = body

        try:
            self.control_socket.sendto(bytes(frame.to_frame()),
                                       (self.remote_ip, self.remote_port))
            received = self.control_socket.recv(1024)
        except socket.error:
            self.control_socket.close()
            self.control_socket = None
            logging.error("KNX/IP gateway did not respond to connect request")
            return False

        # Check if the response is an TUNNELING ACK
        r_sid = received[2] * 256 + received[3]

        if r_sid == KNXIPFrame.CONNECT_RESPONSE:
            self.channel = received[6]
            status = received[7]
            if status == 0:
                hpai = received[8:10]
                logging.debug("Connected KNX IP tunnel " +
                              "(Channel: {}, HPAI: {} {})".format(
                                  self.channel, hpai[0], hpai[1]))
            else:
                logging.error("KNX IP tunnel connect error:" +
                              "(Channel: {}, Status: {})".format(
                                  self.channel, status))
                return False

        else:
            logging.error(
                "Could not initiate tunnel connection, STI = {0:%s}", r_sid)
            return False

        self.connected = True

        return True