Ejemplo n.º 1
0
 def test_cemi_frame(self):
     """Test string representation of KNX/IP CEMI Frame."""
     xknx = XKNX(loop=self.loop)
     cemi_frame = CEMIFrame(xknx)
     cemi_frame.src_addr = GroupAddress("1/2/3")
     cemi_frame.telegram = Telegram(
         group_address=GroupAddress('1/2/5'),
         payload=DPTBinary(7))
     self.assertEqual(
         str(cemi_frame),
         '<CEMIFrame SourceAddress="GroupAddress("1/2/3")" DestinationAddress="GroupAddress("1/2/5")" Flags="1011110011100000" Command="APCIC'
         'ommand.GROUP_WRITE" payload="<DPTBinary value="7" />" />')
Ejemplo n.º 2
0
    def test_telegram_set(self):
        """Test parsing and streaming CEMIFrame KNX/IP packet with DPTArray/DPTTime as payload."""
        telegram = Telegram(
            destination_address=GroupAddress(337),
            payload=GroupValueWrite(
                DPTArray(DPTTime().to_knx(time.strptime(
                    "13:23:42", "%H:%M:%S")))),
        )
        cemi = CEMIFrame(src_addr=IndividualAddress("1.2.2"))
        cemi.telegram = telegram
        cemi.set_hops(5)
        routing_indication = RoutingIndication(cemi=cemi)
        knxipframe = KNXIPFrame.init_from_body(routing_indication)

        raw = bytes.fromhex(
            "06 10 05 30 00 14 29 00 bc d0 12 02 01 51 04 00 80 0d 17 2a")

        assert knxipframe.header.to_knx() == raw[0:6]
        assert knxipframe.body.to_knx() == raw[6:]
        assert knxipframe.to_knx() == raw
Ejemplo n.º 3
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)
Ejemplo n.º 4
0
    async def test_tunnel_request_received_callback(
        self,
        send_cemi_mock,
        send_ack_mock,
    ):
        """Test Tunnel for responding to L_DATA.req with confirmation and indication."""
        # L_Data.req T_Connect from 1.0.250 to 1.0.255 (xknx tunnel endpoint) - ETS Line-Scan
        # communication_channel_id: 0x02   sequence_counter: 0x81
        raw_req = bytes.fromhex("0610 0420 0014 04 02 81 00 1100b06010fa10ff0080")

        _cemi = CEMIFrame()
        _cemi.from_knx(raw_req[10:])
        test_telegram = _cemi.telegram
        test_telegram.direction = TelegramDirection.INCOMING

        response_telegram = Telegram(source_address=self.tunnel._src_address)

        async def tg_received_mock(telegram):
            """Mock for telegram_received_callback."""
            assert telegram == test_telegram
            return [response_telegram]

        self.tunnel.telegram_received_callback = tg_received_mock
        self.tunnel.transport.data_received_callback(raw_req, ("192.168.1.2", 3671))

        await asyncio.sleep(0)
        confirmation_cemi = _cemi
        confirmation_cemi.code = CEMIMessageCode.L_DATA_CON
        response_cemi = CEMIFrame.init_from_telegram(
            response_telegram,
            code=CEMIMessageCode.L_DATA_IND,
            src_addr=self.tunnel._src_address,
        )
        assert send_cemi_mock.call_args_list == [
            call(confirmation_cemi),
            call(response_cemi),
        ]
        send_ack_mock.assert_called_once_with(raw_req[7], raw_req[8])
Ejemplo n.º 5
0
    def test_maximum_apci(self):
        """Test parsing and streaming CEMIFrame KNX/IP packet, testing maximum APCI."""
        telegram = Telegram(
            destination_address=GroupAddress(337),
            payload=GroupValueWrite(DPTBinary(DPTBinary.APCI_MAX_VALUE)),
            source_address=IndividualAddress("1.3.1"),
        )
        xknx = XKNX()

        cemi = CEMIFrame(xknx, src_addr=IndividualAddress("1.3.1"))
        cemi.telegram = telegram
        cemi.set_hops(5)
        routing_indication = RoutingIndication(xknx, cemi=cemi)
        knxipframe = KNXIPFrame.init_from_body(routing_indication)

        raw = bytes.fromhex("0610053000112900BCD0130101510100BF")

        self.assertEqual(knxipframe.to_knx(), list(raw))

        knxipframe2 = KNXIPFrame(xknx)
        knxipframe2.init(KNXIPServiceType.ROUTING_INDICATION)
        knxipframe2.from_knx(knxipframe.to_knx())
        self.assertEqual(knxipframe2.body.cemi.telegram, telegram)
Ejemplo n.º 6
0
    def test_end_to_end_group_write_binary_on(self):
        """Test parsing and streaming CEMIFrame KNX/IP packet, switch on light in my kitchen."""
        # Switch on Kitchen-L1
        raw = bytes.fromhex("0610053000112900BCD0FFF90149010081")
        xknx = XKNX()
        knxipframe = KNXIPFrame(xknx)
        knxipframe.from_knx(raw)
        telegram = knxipframe.body.cemi.telegram
        assert telegram == Telegram(
            destination_address=GroupAddress("329"),
            payload=GroupValueWrite(DPTBinary(1)),
            source_address=IndividualAddress("15.15.249"),
        )

        cemi = CEMIFrame(xknx, src_addr=IndividualAddress("15.15.249"))
        cemi.telegram = telegram
        cemi.set_hops(5)
        routing_indication = RoutingIndication(xknx, cemi=cemi)
        knxipframe2 = KNXIPFrame.init_from_body(routing_indication)

        assert knxipframe2.header.to_knx() == list(raw[0:6])
        assert knxipframe2.body.to_knx() == list(raw[6:])
        assert knxipframe2.to_knx() == list(raw)
Ejemplo n.º 7
0
    def test_end_to_end_group_response(self):
        """Test parsing and streaming CEMIFrame KNX/IP packet, group response."""
        # Incoming state
        raw = bytes.fromhex("0610053000112900BCD013010188010041")
        xknx = XKNX()
        knxipframe = KNXIPFrame(xknx)
        knxipframe.from_knx(raw)
        telegram = knxipframe.body.cemi.telegram
        assert telegram == Telegram(
            destination_address=GroupAddress("392"),
            payload=GroupValueResponse(DPTBinary(1)),
            source_address=IndividualAddress("1.3.1"),
        )

        cemi = CEMIFrame(xknx, src_addr=IndividualAddress("1.3.1"))
        cemi.telegram = telegram
        cemi.set_hops(5)
        routing_indication = RoutingIndication(xknx, cemi=cemi)
        knxipframe2 = KNXIPFrame.init_from_body(routing_indication)

        assert knxipframe2.header.to_knx() == list(raw[0:6])
        assert knxipframe2.body.to_knx() == list(raw[6:])
        assert knxipframe2.to_knx() == list(raw)
Ejemplo n.º 8
0
    def test_end_to_end_group_read(self):
        """Test parsing and streaming CEMIFrame KNX/IP packet, group read."""
        # State request
        raw = bytes.fromhex("0610053000112900BCD0FFF901B8010000")
        xknx = XKNX()
        knxipframe = KNXIPFrame(xknx)
        knxipframe.from_knx(raw)
        telegram = knxipframe.body.cemi.telegram
        assert telegram == Telegram(
            destination_address=GroupAddress("440"),
            payload=GroupValueRead(),
            source_address=IndividualAddress("15.15.249"),
        )

        cemi = CEMIFrame(xknx, src_addr=IndividualAddress("15.15.249"))
        cemi.telegram = telegram
        cemi.set_hops(5)
        routing_indication = RoutingIndication(xknx, cemi=cemi)
        knxipframe2 = KNXIPFrame.init_from_body(routing_indication)

        assert knxipframe2.header.to_knx() == list(raw[0:6])
        assert knxipframe2.body.to_knx() == list(raw[6:])
        assert knxipframe2.to_knx() == list(raw)
Ejemplo n.º 9
0
    def test_end_to_end_group_write_2bytes(self):
        """Test parsing and streaming CEMIFrame KNX/IP packet, setting value of thermostat."""
        # Incoming Temperature from thermostat
        raw = bytes.fromhex("0610053000132900BCD01402080103008007C1")
        xknx = XKNX()
        knxipframe = KNXIPFrame(xknx)
        knxipframe.from_knx(raw)
        telegram = knxipframe.body.cemi.telegram
        assert telegram == Telegram(
            destination_address=GroupAddress("2049"),
            payload=GroupValueWrite(DPTArray(DPTTemperature().to_knx(19.85))),
            source_address=IndividualAddress("1.4.2"),
        )

        cemi = CEMIFrame(xknx, src_addr=IndividualAddress("1.4.2"))
        cemi.telegram = telegram
        cemi.set_hops(5)
        routing_indication = RoutingIndication(xknx, cemi=cemi)
        knxipframe2 = KNXIPFrame.init_from_body(routing_indication)

        assert knxipframe2.header.to_knx() == list(raw[0:6])
        assert knxipframe2.body.to_knx() == list(raw[6:])
        assert knxipframe2.to_knx() == list(raw)
Ejemplo n.º 10
0
    def test_end_to_end_group_write_1byte(self):
        """Test parsing and streaming CEMIFrame KNX/IP packet, dimm light in my kitchen."""
        # Dimm Kitchen L1 to 0x65
        raw = bytes.fromhex(
            "06 10 05 30 00 12 29 00 bc d0 ff f9 01 4b 02 00 80 65")
        knxipframe = KNXIPFrame()
        knxipframe.from_knx(raw)
        telegram = knxipframe.body.cemi.telegram
        assert telegram == Telegram(
            destination_address=GroupAddress("331"),
            payload=GroupValueWrite(DPTArray(0x65)),
            source_address=IndividualAddress("15.15.249"),
        )

        cemi = CEMIFrame(src_addr=IndividualAddress("15.15.249"))
        cemi.telegram = telegram
        cemi.set_hops(5)
        routing_indication = RoutingIndication(cemi=cemi)
        knxipframe2 = KNXIPFrame.init_from_body(routing_indication)

        assert knxipframe2.header.to_knx() == raw[0:6]
        assert knxipframe2.body.to_knx() == raw[6:]
        assert knxipframe2.to_knx() == raw
Ejemplo n.º 11
0
Archivo: tunnel.py Proyecto: XKNX/xknx
    async def handle_cemi_frame(self, cemi: CEMIFrame) -> None:
        """Handle incoming telegram and send responses if applicable (device management)."""
        telegram = cemi.telegram
        telegram.direction = TelegramDirection.INCOMING

        if response_tgs := await self.telegram_received_callback(telegram):
            response_code = (CEMIMessageCode.L_DATA_IND
                             if cemi.code is CEMIMessageCode.L_DATA_REQ else
                             CEMIMessageCode.L_DATA_REQ)
            for response in response_tgs:
                await self._send_cemi(
                    CEMIFrame.init_from_telegram(
                        telegram=response,
                        code=response_code,
                        src_addr=self._src_address,
                    ))
Ejemplo n.º 12
0
    async def _tunnelling_request(self, telegram: Telegram) -> bool:
        """Send Telegram to tunnelling device."""
        if self.communication_channel is None:
            raise CommunicationError(
                "Sending telegram failed. No active communication channel.")
        cemi = CEMIFrame.init_from_telegram(
            telegram=telegram,
            code=CEMIMessageCode.L_DATA_REQ,
            src_addr=self._src_address,
        )
        tunnelling_request = TunnellingRequest(
            communication_channel_id=self.communication_channel,
            sequence_counter=self.sequence_number,
            cemi=cemi,
        )

        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(),
            telegram=telegram,
        )
        return True
Ejemplo n.º 13
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
Ejemplo n.º 14
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()