Exemple #1
0
 def test_tunnelling_request(self):
     """Test string representation of KNX/IP TunnellingRequest."""
     xknx = XKNX(loop=self.loop)
     tunnelling_request = TunnellingRequest(xknx)
     tunnelling_request.communication_channel_id = 23
     tunnelling_request.sequence_counter = 42
     self.assertEqual(
         str(tunnelling_request),
         '<TunnellingRequest communication_channel_id="23" sequence_counter="42" cemi="<CEMIFrame SourceAddress="GroupAddress("0/0/0")" Destina'
         'tionAddress="GroupAddress("0/0/0")" Flags="               0" Command="APCICommand.GROUP_READ" payload="None" />" />')
Exemple #2
0
 def test_tunnelling_request(self):
     """Test string representation of KNX/IP TunnellingRequest."""
     tunnelling_request = TunnellingRequest()
     tunnelling_request.communication_channel_id = 23
     tunnelling_request.sequence_counter = 42
     assert (
         str(tunnelling_request) ==
         '<TunnellingRequest communication_channel_id="23" sequence_counter="42" '
         'cemi="<CEMIFrame SourceAddress="IndividualAddress("0.0.0")" DestinationAddress="GroupAddress("0/0/0")" '
         'Flags="               0" code="L_DATA_REQ" payload="None" />" />')
Exemple #3
0
 def test_tunnelling_request(self):
     """Test string representation of KNX/IP TunnellingRequest."""
     xknx = XKNX(loop=self.loop)
     tunnelling_request = TunnellingRequest(xknx)
     tunnelling_request.communication_channel_id = 23
     tunnelling_request.sequence_counter = 42
     self.assertEqual(
         str(tunnelling_request),
         '<TunnellingRequest communication_channel_id="23" sequence_counter="42" cemi="<CEMIFrame SourceAddress="GroupAddress("0/0/0")" Destina'
         'tionAddress="GroupAddress("0/0/0")" Flags="               0" Command="APCICommand.GROUP_READ" payload="None" />" />')
Exemple #4
0
 def test_tunnelling_request(self):
     """Test string representation of KNX/IP TunnellingRequest."""
     xknx = XKNX()
     tunnelling_request = TunnellingRequest(xknx)
     tunnelling_request.communication_channel_id = 23
     tunnelling_request.sequence_counter = 42
     self.assertEqual(
         str(tunnelling_request),
         '<TunnellingRequest communication_channel_id="23" sequence_counter="42" cemi="<CEMIFrame SourceAddress="IndividualAddress("0.0.0")"'
         ' DestinationAddress="GroupAddress("0/0/0")" Flags="               0" payload="None" />" />',
     )
Exemple #5
0
    async def test_tunnel_wait_for_l2_confirmation(self, time_travel):
        """Test tunnel waits for L_DATA.con before sending another L_DATA.req."""
        self.tunnel.transport.send = Mock()
        self.tunnel.communication_channel = 1

        test_telegram = Telegram(payload=GroupValueWrite(DPTArray((1,))))
        test_ack = KNXIPFrame.init_from_body(TunnellingAck(sequence_counter=23))
        confirmation = KNXIPFrame.init_from_body(
            TunnellingRequest(
                communication_channel_id=1,
                sequence_counter=23,
                cemi=CEMIFrame.init_from_telegram(
                    test_telegram, code=CEMIMessageCode.L_DATA_CON
                ),
            )
        )
        task = asyncio.create_task(self.tunnel.send_telegram(test_telegram))
        await time_travel(0)
        self.tunnel.transport.handle_knxipframe(test_ack, HPAI())
        await time_travel(0)
        assert not task.done()
        assert self.tunnel.transport.send.call_count == 1
        self.tunnel.transport.handle_knxipframe(confirmation, HPAI())
        await time_travel(0)
        assert task.done()
        # one call for the outgoing request and one for the ACK for the confirmation
        assert self.tunnel.transport.send.call_count == 2
        await task
Exemple #6
0
    async def _tunnelling_request(self, cemi: CEMIFrame) -> bool:
        """Send Telegram to tunnelling device."""
        if self.communication_channel is None:
            raise CommunicationError(
                "Sending telegram failed. No active communication channel.")
        tunnelling_request = TunnellingRequest(
            communication_channel_id=self.communication_channel,
            sequence_counter=self.sequence_number,
            cemi=cemi,
        )

        if cemi.code is CEMIMessageCode.L_DATA_REQ:

            async def _async_wrapper() -> None:
                self.transport.send(
                    KNXIPFrame.init_from_body(tunnelling_request))

            await self._wait_for_tunnelling_request_confirmation(
                send_tunneling_request_aw=_async_wrapper(),
                cemi=cemi,
            )
        else:
            self.transport.send(KNXIPFrame.init_from_body(tunnelling_request))

        return True
Exemple #7
0
    def test_connect_request(self):
        """Test parsing and streaming connection tunneling request KNX/IP packet."""
        raw = bytes.fromhex(
            "06 10 04 20 00 15 04 01 17 00 11 00 BC E0 00 00 48 08 01 00 81")
        knxipframe = KNXIPFrame()
        knxipframe.from_knx(raw)

        assert isinstance(knxipframe.body, TunnellingRequest)
        assert knxipframe.body.communication_channel_id == 1
        assert knxipframe.body.sequence_counter == 23
        assert isinstance(knxipframe.body.cemi, CEMIFrame)

        assert knxipframe.body.cemi.telegram == Telegram(
            destination_address=GroupAddress("9/0/8"),
            payload=GroupValueWrite(DPTBinary(1)),
        )

        cemi = CEMIFrame(code=CEMIMessageCode.L_DATA_REQ)
        cemi.telegram = Telegram(
            destination_address=GroupAddress("9/0/8"),
            payload=GroupValueWrite(DPTBinary(1)),
        )
        tunnelling_request = TunnellingRequest(
            communication_channel_id=1,
            sequence_counter=23,
            cemi=cemi,
        )
        knxipframe2 = KNXIPFrame.init_from_body(tunnelling_request)

        assert knxipframe2.to_knx() == raw
Exemple #8
0
 def create_knxipframe(self) -> KNXIPFrame:
     """Create KNX/IP Frame object to be sent to device."""
     tunnelling_request = TunnellingRequest(
         communication_channel_id=self.communication_channel_id,
         sequence_counter=self.sequence_counter,
         cemi=self.cemi_frame,
     )
     return KNXIPFrame.init_from_body(tunnelling_request)
    def test_connect_request(self):
        """Test parsing and streaming connection tunneling request KNX/IP packet."""
        raw = (
            0x06,
            0x10,
            0x04,
            0x20,
            0x00,
            0x15,
            0x04,
            0x01,
            0x17,
            0x00,
            0x11,
            0x00,
            0xBC,
            0xE0,
            0x00,
            0x00,
            0x48,
            0x08,
            0x01,
            0x00,
            0x81,
        )
        xknx = XKNX()
        knxipframe = KNXIPFrame(xknx)
        knxipframe.from_knx(raw)

        self.assertTrue(isinstance(knxipframe.body, TunnellingRequest))
        self.assertEqual(knxipframe.body.communication_channel_id, 1)
        self.assertEqual(knxipframe.body.sequence_counter, 23)
        self.assertTrue(isinstance(knxipframe.body.cemi, CEMIFrame))

        self.assertEqual(
            knxipframe.body.cemi.telegram,
            Telegram(
                destination_address=GroupAddress("9/0/8"),
                payload=GroupValueWrite(DPTBinary(1)),
            ),
        )

        cemi = CEMIFrame(xknx, code=CEMIMessageCode.L_Data_REQ)
        cemi.telegram = Telegram(
            destination_address=GroupAddress("9/0/8"),
            payload=GroupValueWrite(DPTBinary(1)),
        )
        tunnelling_request = TunnellingRequest(xknx,
                                               communication_channel_id=1,
                                               sequence_counter=23,
                                               cemi=cemi)
        knxipframe2 = KNXIPFrame.init_from_body(tunnelling_request)

        self.assertEqual(knxipframe2.to_knx(), list(raw))
Exemple #10
0
 def create_knxipframe(self) -> KNXIPFrame:
     """Create KNX/IP Frame object to be sent to device."""
     cemi = CEMIFrame.init_from_telegram(
         telegram=self.telegram,
         code=CEMIMessageCode.L_DATA_REQ,
         src_addr=self.src_address,
     )
     tunnelling_request = TunnellingRequest(
         communication_channel_id=self.communication_channel_id,
         sequence_counter=self.sequence_counter,
         cemi=cemi,
     )
     return KNXIPFrame.init_from_body(tunnelling_request)
Exemple #11
0
    async def test_tunnelling(self):
        """Test tunnelling from KNX bus."""
        communication_channel_id = 23
        data_endpoint = ("192.168.1.2", 4567)
        udp_transport = UDPTransport(("192.168.1.1", 0), ("192.168.1.2", 1234))
        cemi = CEMIFrame.init_from_telegram(
            Telegram(
                destination_address=GroupAddress("1/2/3"),
                payload=GroupValueWrite(DPTArray((0x1, 0x2, 0x3))),
            )
        )
        sequence_counter = 42
        tunnelling = Tunnelling(
            udp_transport,
            data_endpoint,
            cemi,
            sequence_counter,
            communication_channel_id,
        )
        tunnelling.timeout_in_seconds = 0

        assert tunnelling.awaited_response_class == TunnellingAck
        assert tunnelling.communication_channel_id == communication_channel_id

        # Expected KNX/IP-Frame:
        tunnelling_request = TunnellingRequest(
            communication_channel_id=communication_channel_id,
            sequence_counter=sequence_counter,
        )
        tunnelling_request.cemi = cemi
        exp_knxipframe = KNXIPFrame.init_from_body(tunnelling_request)
        with patch("xknx.io.transport.UDPTransport.send") as mock_udp_send, patch(
            "xknx.io.transport.UDPTransport.getsockname"
        ) as mock_udp_getsockname:
            mock_udp_getsockname.return_value = ("192.168.1.3", 4321)
            await tunnelling.start()
            mock_udp_send.assert_called_with(exp_knxipframe, addr=data_endpoint)

        # Response KNX/IP-Frame with wrong type
        wrong_knxipframe = KNXIPFrame()
        wrong_knxipframe.init(KNXIPServiceType.CONNECTIONSTATE_REQUEST)
        with patch("logging.Logger.warning") as mock_warning:
            tunnelling.response_rec_callback(wrong_knxipframe, HPAI(), None)
            mock_warning.assert_called_with("Could not understand knxipframe")

        # Response KNX/IP-Frame with error:
        err_knxipframe = KNXIPFrame()
        err_knxipframe.init(KNXIPServiceType.TUNNELLING_ACK)
        err_knxipframe.body.status_code = ErrorCode.E_CONNECTION_ID
        with patch("logging.Logger.debug") as mock_warning:
            tunnelling.response_rec_callback(err_knxipframe, HPAI(), None)
            mock_warning.assert_called_with(
                "Error: KNX bus responded to request of type '%s' with error in '%s': %s",
                type(tunnelling).__name__,
                type(err_knxipframe.body).__name__,
                ErrorCode.E_CONNECTION_ID,
            )

        # Correct Response KNX/IP-Frame:
        res_knxipframe = KNXIPFrame()
        res_knxipframe.init(KNXIPServiceType.TUNNELLING_ACK)
        tunnelling.response_rec_callback(res_knxipframe, HPAI(), None)
        assert tunnelling.success
Exemple #12
0
    def test_tunnelling(self):
        """Test tunnelling from KNX bus."""
        # pylint: disable=too-many-locals
        xknx = XKNX()
        communication_channel_id = 23
        udp_client = UDPClient(xknx, ("192.168.1.1", 0), ("192.168.1.2", 1234))
        telegram = Telegram(
            destination_address=GroupAddress("1/2/3"),
            payload=GroupValueWrite(DPTArray((0x1, 0x2, 0x3))),
        )
        sequence_counter = 42
        src_address = IndividualAddress("2.2.2")
        tunnelling = Tunnelling(
            xknx,
            udp_client,
            telegram,
            src_address,
            sequence_counter,
            communication_channel_id,
        )
        tunnelling.timeout_in_seconds = 0

        self.assertEqual(tunnelling.awaited_response_class, TunnellingAck)
        self.assertEqual(tunnelling.communication_channel_id,
                         communication_channel_id)

        # Expected KNX/IP-Frame:
        tunnelling_request = TunnellingRequest(
            xknx,
            communication_channel_id=communication_channel_id,
            sequence_counter=sequence_counter,
        )
        tunnelling_request.cemi.telegram = telegram
        tunnelling_request.cemi.src_addr = src_address
        exp_knxipframe = KNXIPFrame.init_from_body(tunnelling_request)
        with patch("xknx.io.UDPClient.send") as mock_udp_send, patch(
                "xknx.io.UDPClient.getsockname") as mock_udp_getsockname:
            mock_udp_getsockname.return_value = ("192.168.1.3", 4321)
            self.loop.run_until_complete(tunnelling.start())
            mock_udp_send.assert_called_with(exp_knxipframe)

        # Response KNX/IP-Frame with wrong type
        wrong_knxipframe = KNXIPFrame(xknx)
        wrong_knxipframe.init(KNXIPServiceType.CONNECTIONSTATE_REQUEST)
        with patch("logging.Logger.warning") as mock_warning:
            tunnelling.response_rec_callback(wrong_knxipframe, None)
            mock_warning.assert_called_with("Could not understand knxipframe")

        # Response KNX/IP-Frame with error:
        err_knxipframe = KNXIPFrame(xknx)
        err_knxipframe.init(KNXIPServiceType.TUNNELLING_ACK)
        err_knxipframe.body.status_code = ErrorCode.E_CONNECTION_ID
        with patch("logging.Logger.debug") as mock_warning:
            tunnelling.response_rec_callback(err_knxipframe, None)
            mock_warning.assert_called_with(
                "Error: KNX bus responded to request of type '%s' with error in '%s': %s",
                type(tunnelling).__name__,
                type(err_knxipframe.body).__name__,
                ErrorCode.E_CONNECTION_ID,
            )

        # Correct Response KNX/IP-Frame:
        res_knxipframe = KNXIPFrame(xknx)
        res_knxipframe.init(KNXIPServiceType.TUNNELLING_ACK)
        tunnelling.response_rec_callback(res_knxipframe, None)
        self.assertTrue(tunnelling.success)
Exemple #13
0
    async def test_tunnel_connect_send_disconnect(
        self, time_travel, route_back, data_endpoint_addr, local_endpoint
    ):
        """Test initiating a tunnelling connection."""
        local_addr = ("192.168.1.1", 12345)
        remote_addr = ("192.168.1.2", 3671)
        self.tunnel.route_back = route_back
        gateway_data_endpoint = (
            HPAI(*data_endpoint_addr) if data_endpoint_addr else HPAI()
        )
        self.tunnel.transport.connect = AsyncMock()
        self.tunnel.transport.getsockname = Mock(return_value=local_addr)
        self.tunnel.transport.send = Mock()
        self.tunnel.transport.stop = Mock()

        # Connect
        connect_request = ConnectRequest(
            control_endpoint=local_endpoint,
            data_endpoint=local_endpoint,
        )
        connect_frame = KNXIPFrame.init_from_body(connect_request)

        connection_task = asyncio.create_task(self.tunnel.connect())
        await time_travel(0)
        self.tunnel.transport.connect.assert_called_once()
        self.tunnel.transport.send.assert_called_once_with(connect_frame)

        connect_response_frame = KNXIPFrame.init_from_body(
            ConnectResponse(
                communication_channel=23,
                data_endpoint=gateway_data_endpoint,
                identifier=7,
            )
        )
        self.tunnel.transport.handle_knxipframe(connect_response_frame, remote_addr)
        await connection_task
        assert self.tunnel._data_endpoint_addr == data_endpoint_addr
        assert self.tunnel._src_address == IndividualAddress(7)

        # Send - use data endpoint
        self.tunnel.transport.send.reset_mock()
        test_telegram = Telegram(payload=GroupValueWrite(DPTArray((1,))))
        test_telegram_frame = KNXIPFrame.init_from_body(
            TunnellingRequest(
                communication_channel_id=23,
                sequence_counter=0,
                cemi=CEMIFrame.init_from_telegram(
                    test_telegram,
                    code=CEMIMessageCode.L_DATA_REQ,
                    src_addr=IndividualAddress(7),
                ),
            )
        )
        asyncio.create_task(self.tunnel.send_telegram(test_telegram))
        await time_travel(0)
        self.tunnel.transport.send.assert_called_once_with(
            test_telegram_frame, addr=data_endpoint_addr
        )
        # skip ack and confirmation

        # Disconnect
        self.tunnel.transport.send.reset_mock()
        disconnect_request = DisconnectRequest(
            communication_channel_id=23,
            control_endpoint=local_endpoint,
        )
        disconnect_frame = KNXIPFrame.init_from_body(disconnect_request)

        disconnection_task = asyncio.create_task(self.tunnel.disconnect())
        await time_travel(0)
        self.tunnel.transport.send.assert_called_once_with(disconnect_frame)

        disconnect_response_frame = KNXIPFrame.init_from_body(
            DisconnectResponse(communication_channel_id=23)
        )
        self.tunnel.transport.handle_knxipframe(disconnect_response_frame, remote_addr)
        await disconnection_task
        assert self.tunnel._data_endpoint_addr is None
        self.tunnel.transport.stop.assert_called_once()