def hpai_body(self): """ Create a body with HPAI information. This is used for disconnect and connection state requests. """ body = [] # ============ IP Body ========== body.extend([self.channel]) # Communication Channel Id body.extend([0x00]) # Reserverd # =========== Client HPAI =========== body.extend([0x08]) # HPAI Length body.extend([0x01]) # Host Protocol # Tunnel Client Socket IP body.extend(ip_to_array(self.control_socket.getsockname()[0])) # Tunnel Client Socket Port body.extend(int_to_array(self.control_socket.getsockname()[1])) return body
def _build_search_request_data(requestor_ipaddress, requestor_port): """ Build a KNX IP Search Request Broadcast Package :param requestor_ipaddress: IP Address of the Search Request Response Receiver :param requestor_port: Port of the Search Request Response Receiver :return: Complete Search Request Broadcast Package :rtype: bytes """ req = [] req.extend([0x06]) # HeaderSize req.extend([0x10]) # KNXNetIP Version req.extend([0x02, 0x01]) # Search Reques°t req.extend([0x00, 0x0E]) # HEADER_SIZE_10 + sizeof(HPAI) # ==== Discovery Endpoint HPAI ==== req.extend([0x08]) # Struct Length req.extend([0x01]) # Host Protocol Code = 0x01 = IPV4_UDP req.extend(ip_to_array(requestor_ipaddress)) req.extend(int_to_array(requestor_port)) return bytes(req)
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
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 = 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