Exemplo n.º 1
0
    def from_knx_data_link_layer(self, cemi: bytes) -> int:
        """Parse L_DATA_IND, CEMIMessageCode.L_DATA_REQ, CEMIMessageCode.L_DATA_CON."""
        if len(cemi) < 10:
            raise UnsupportedCEMIMessage(
                f"CEMI too small. Length: {len(cemi)}; CEMI: {cemi.hex()}")

        # AddIL (Additional Info Length), as specified within
        # KNX Chapter 3.6.3/4.1.4.3 "Additional information."
        # Additional information is not yet parsed.
        addil = cemi[1]
        # Control field 1 and Control field 2 - first 2 octets after Additional information
        self.flags = cemi[2 + addil] * 256 + cemi[3 + addil]

        self.src_addr = IndividualAddress((cemi[4 + addil], cemi[5 + addil]))

        dst_is_group_address = bool(self.flags
                                    & CEMIFlags.DESTINATION_GROUP_ADDRESS)
        dst_raw_address = (cemi[6 + addil], cemi[7 + addil])
        self.dst_addr = (GroupAddress(dst_raw_address) if dst_is_group_address
                         else IndividualAddress(dst_raw_address))

        npdu_len = cemi[8 + addil]

        apdu = cemi[9 + addil:]
        if len(apdu) != (npdu_len +
                         1):  # TCPI octet not included in NPDU length
            raise CouldNotParseKNXIP(
                f"APDU LEN should be {npdu_len} but is {len(apdu) - 1} in CEMI: {cemi.hex()}"
            )

        # TPCI (transport layer control information)
        # - with control bit set -> 8 bit; no APDU
        # - no control bit set (data) -> First 6 bit
        # APCI (application layer control information) -> Last  10 bit of TPCI/APCI
        try:
            self.tpci = TPCI.resolve(raw_tpci=cemi[9 + addil],
                                     dst_is_group_address=dst_is_group_address)
        except ConversionError as err:
            raise UnsupportedCEMIMessage(
                f"TPCI not supported: {cemi[9 + addil]:#10b}") from err

        if self.tpci.control:
            if npdu_len:
                raise UnsupportedCEMIMessage(
                    f"Invalid length for control TPDU {self.tpci}: {npdu_len}")
            return 10 + addil

        _apci = apdu[0] * 256 + apdu[1]
        try:
            self.payload = APCI.resolve_apci(_apci)
        except ConversionError as err:
            raise UnsupportedCEMIMessage(
                f"APCI not supported: {_apci:#012b}") from err

        self.payload.from_knx(apdu)

        return 10 + addil + npdu_len
Exemplo n.º 2
0
    def from_knx_data_link_layer(self, cemi):
        """Parse L_DATA_IND, CEMIMessageCode.L_Data_REQ, CEMIMessageCode.L_DATA_CON."""
        if len(cemi) < 11:
            # eg. ETS Line-Scan issues L_DATA_IND with length 10
            raise UnsupportedCEMIMessage(
                "CEMI too small. Length: {}; CEMI: {}".format(len(cemi), cemi)
            )

        # AddIL (Additional Info Length), as specified within
        # KNX Chapter 3.6.3/4.1.4.3 "Additional information."
        # Additional information is not yet parsed.
        addil = cemi[1]
        # Control field 1 and Control field 2 - first 2 octets after Additional information
        self.flags = cemi[2 + addil] * 256 + cemi[3 + addil]

        self.src_addr = PhysicalAddress((cemi[4 + addil], cemi[5 + addil]))

        if self.flags & CEMIFlags.DESTINATION_GROUP_ADDRESS:
            self.dst_addr = GroupAddress(
                (cemi[6 + addil], cemi[7 + addil]), levels=self.xknx.address_format
            )
        else:
            self.dst_addr = PhysicalAddress((cemi[6 + addil], cemi[7 + addil]))

        self.mpdu_len = cemi[8 + addil]

        # TPCI (transport layer control information)   -> First 14 bit
        # APCI (application layer control information) -> Last  10 bit

        tpci_apci = cemi[9 + addil] * 256 + cemi[10 + addil]

        try:
            self.cmd = APCICommand(tpci_apci & 0xFFC0)
        except ValueError:
            raise UnsupportedCEMIMessage(
                "APCI not supported: {:#012b}".format(tpci_apci & 0xFFC0)
            )

        apdu = cemi[10 + addil :]
        if len(apdu) != self.mpdu_len:
            raise CouldNotParseKNXIP(
                "APDU LEN should be {} but is {}".format(self.mpdu_len, len(apdu))
            )

        if len(apdu) == 1:
            apci = tpci_apci & DPTBinary.APCI_BITMASK
            self.payload = DPTBinary(apci)
        else:
            self.payload = DPTArray(cemi[11 + addil :])

        return 10 + addil + len(apdu)
Exemplo n.º 3
0
    def from_knx_data_link_layer(self, cemi: bytes) -> int:
        """Parse L_DATA_IND, CEMIMessageCode.L_DATA_REQ, CEMIMessageCode.L_DATA_CON."""
        if len(cemi) < 11:
            # eg. ETS Line-Scan issues L_DATA_IND with length 10
            raise UnsupportedCEMIMessage(
                "CEMI too small. Length: {}; CEMI: {}".format(len(cemi), cemi.hex())
            )

        # AddIL (Additional Info Length), as specified within
        # KNX Chapter 3.6.3/4.1.4.3 "Additional information."
        # Additional information is not yet parsed.
        addil = cemi[1]
        # Control field 1 and Control field 2 - first 2 octets after Additional information
        self.flags = cemi[2 + addil] * 256 + cemi[3 + addil]

        self.src_addr = IndividualAddress((cemi[4 + addil], cemi[5 + addil]))

        if self.flags & CEMIFlags.DESTINATION_GROUP_ADDRESS:
            self.dst_addr = GroupAddress(
                (cemi[6 + addil], cemi[7 + addil]), levels=self.xknx.address_format
            )
        else:
            self.dst_addr = IndividualAddress((cemi[6 + addil], cemi[7 + addil]))

        self.mpdu_len = cemi[8 + addil]

        # TPCI (transport layer control information)   -> First 14 bit
        # APCI (application layer control information) -> Last  10 bit

        apdu = cemi[9 + addil :]
        if len(apdu) != (self.mpdu_len + 1):
            raise CouldNotParseKNXIP(
                "APDU LEN should be {} but is {} in CEMI: {}".format(
                    self.mpdu_len, len(apdu), cemi.hex()
                )
            )

        tpci_apci = (apdu[0] << 8) + apdu[1]

        try:
            self.payload = APCI.resolve_apci(tpci_apci & 0x03FF)
        except ConversionError:
            raise UnsupportedCEMIMessage(
                "APCI not supported: {:#012b}".format(tpci_apci & 0x03FF)
            )

        self.payload.from_knx(apdu)

        return 10 + addil + self.mpdu_len
Exemplo n.º 4
0
    def from_knx(self, raw):
        """Parse/deserialize from KNX/IP raw data."""
        try:
            try:
                self.code = CEMIMessageCode(raw[0])
            except ValueError:
                raise UnsupportedCEMIMessage("CEMIMessageCode not implemented: {0} ".format(raw[0]))

            if self.code == CEMIMessageCode.L_DATA_IND or \
                    self.code == CEMIMessageCode.L_Data_REQ or \
                    self.code == CEMIMessageCode.L_DATA_CON:
                return self.from_knx_data_link_layer(raw)
            raise UnsupportedCEMIMessage("Could not handle CEMIMessageCode: {0} / {1}".format(self.code, raw[0]))
        except UnsupportedCEMIMessage as unsupported_cemi_err:
            # self.xknx.logger.warning("Ignoring not implemented CEMI: %s", unsupported_cemi_err)
            return len(raw)
Exemplo n.º 5
0
    def from_knx(self, raw: bytes) -> int:
        """Parse/deserialize from KNX/IP raw data."""
        try:
            self.code = CEMIMessageCode(raw[0])
        except ValueError:
            raise UnsupportedCEMIMessage(
                f"CEMIMessageCode not implemented: {raw[0]} in CEMI: {raw.hex()}"
            )

        if self.code not in (
                CEMIMessageCode.L_DATA_IND,
                CEMIMessageCode.L_DATA_REQ,
                CEMIMessageCode.L_DATA_CON,
        ):
            raise UnsupportedCEMIMessage(
                f"Could not handle CEMIMessageCode: {self.code} / {raw[0]} in CEMI: {raw.hex()}"
            )

        return self.from_knx_data_link_layer(raw)
Exemplo n.º 6
0
    def from_knx(self, raw):
        """Parse/deserialize from KNX/IP raw data."""
        try:
            self.code = CEMIMessageCode(raw[0])
        except ValueError:
            raise UnsupportedCEMIMessage(
                "CEMIMessageCode not implemented: {} ".format(raw[0])
            )

        if self.code not in (
            CEMIMessageCode.L_DATA_IND,
            CEMIMessageCode.L_Data_REQ,
            CEMIMessageCode.L_DATA_CON,
        ):
            raise UnsupportedCEMIMessage(
                "Could not handle CEMIMessageCode: {} / {}".format(self.code, raw[0])
            )

        return self.from_knx_data_link_layer(raw)