Esempio n. 1
0
    def __init__(self, value: int | tuple[int]) -> None:
        """Initialize DPTBinary class."""
        if isinstance(value, tuple):
            value = value[0]
        if not isinstance(value, int):
            raise TypeError()
        if value > DPTBinary.APCI_BITMASK or value < 0:
            raise ConversionError("Could not init DPTBinary", value=str(value))

        self.value = value
Esempio n. 2
0
    def to_knx(self):
        """Serialize to KNX/IP raw data."""
        if not isinstance(self.src_addr, Address):
            raise ConversionError("src_add not set")
        if not isinstance(self.dst_addr, Address):
            raise ConversionError("dst_add not set")

        data = []

        data.append(self.code.value)
        data.append(0x00)
        data.append((self.flags >> 8) & 255)
        data.append(self.flags & 255)
        data.extend(self.src_addr.to_knx())
        data.extend(self.dst_addr.to_knx())

        def encode_cmd_and_payload(cmd,
                                   encoded_payload=0,
                                   appended_payload=None):
            """Encode cmd and payload."""
            if appended_payload is None:
                appended_payload = []
            data = [
                1 + len(appended_payload), (cmd.value >> 8) & 0xff,
                (cmd.value & 0xff) | (encoded_payload & DPTBinary.APCI_BITMASK)
            ]
            data.extend(appended_payload)
            return data

        if self.payload is None:
            data.extend(encode_cmd_and_payload(self.cmd))
        elif isinstance(self.payload, DPTBinary):
            data.extend(
                encode_cmd_and_payload(self.cmd,
                                       encoded_payload=self.payload.value))
        elif isinstance(self.payload, DPTArray):
            data.extend(
                encode_cmd_and_payload(self.cmd,
                                       appended_payload=self.payload.value))
        else:
            raise TypeError()
        return data
Esempio n. 3
0
 def resolve_telegram_type(cmd):
     """Return telegram type from APCI Command."""
     if cmd == APCICommand.GROUP_WRITE:
         return TelegramType.GROUP_WRITE
     elif cmd == APCICommand.GROUP_READ:
         return TelegramType.GROUP_READ
     elif cmd == APCICommand.GROUP_RESPONSE:
         return TelegramType.GROUP_RESPONSE
     else:
         raise ConversionError(
             "Telegram not implemented for {0}".format(self.cmd))
Esempio n. 4
0
    def from_knx(cls, raw, invert: bool = False):
        """Parse/deserialize from KNX/IP raw data."""
        if not cls._test_boundaries(raw):
            raise ConversionError("Cant parse %s" % cls.__name__, raw=raw)

        control, step_code = cls._decode(raw)

        if invert:
            control = 0 if control > 0 else 1

        return {"control": control, "step_code": step_code}
Esempio n. 5
0
 def test_bytesarray(cls, raw):
     """Test if array of raw bytes has the correct length and values of correct type."""
     if cls.payload_length is None:
         raise NotImplementedError(
             "payload_length has to be defined for: %s" % cls)
     if (not isinstance(raw,
                        (tuple, list)) or len(raw) != cls.payload_length
             or any(not isinstance(byte, int) for byte in raw)
             or any(byte < 0 for byte in raw) or any(byte > 255
                                                     for byte in raw)):
         raise ConversionError("Invalid raw bytes", raw=raw)
Esempio n. 6
0
    def _parse_pattern(self, pattern: str) -> None:
        if pattern.startswith("i"):
            self.internal_group_address_pattern = InternalGroupAddress(
                pattern).address
            return

        for part in pattern.split("/"):
            self.level_filters.append(AddressFilter.LevelFilter(part))
        if len(self.level_filters) > 3:
            raise ConversionError("Too many parts within pattern.",
                                  pattern=pattern)
Esempio n. 7
0
 def to_knx(cls, value: int | float) -> tuple[int]:
     """Serialize to KNX/IP raw data."""
     try:
         knx_value = int(value)
         if not cls._test_boundaries(knx_value):
             raise ValueError
         if knx_value < 0:
             knx_value += 0x100
         return (knx_value & 0xFF,)
     except ValueError:
         raise ConversionError(f"Could not serialize {cls.__name__}", value=value)
Esempio n. 8
0
 def to_knx(self, value: Any) -> DPTBinary:
     """Convert value to payload."""
     if isinstance(value, HVACControllerMode):
         # foreign operation modes will set the RemoteValue to False
         return DPTBinary(value == self.controller_mode)
     raise ConversionError(
         "value invalid",
         value=value,
         device_name=self.device_name,
         feature_name=self.feature_name,
     )
Esempio n. 9
0
File: tpci.py Progetto: XKNX/xknx
    def resolve(raw_tpci: int, dst_is_group_address: bool) -> TPCI:
        """
        Return TPCI instance from TPCI command.

        See KNX Specifications 03_03_04 Transport Layer v01.02.02 AS §2 TPDU
        """
        control = raw_tpci & CONTROL_BIT_MASK
        numbered = raw_tpci & NUMBERED_BIT_MASK
        sequence_number = (raw_tpci >> 2) & 0xF

        if dst_is_group_address:
            if control or numbered:
                raise ConversionError("Invalid TPCI flags in group addressed frame.")
            if not sequence_number:
                return TDataGroup()
            if sequence_number == 1:
                # TDataTagGroup uses sequence number field as flag
                return TDataTagGroup()

        if not numbered and sequence_number:
            raise ConversionError("Sequence number not allowed for unnumbered TPCI")

        if not control:
            # data - last 2 bits are part of APCI
            if numbered:
                return TDataConnected(sequence_number=sequence_number)
            return TDataIndividual()
        # unnumbered control
        control_flags = raw_tpci & 0b11
        if not numbered:
            if control_flags == 0:
                return TConnect()
            if control_flags == 1:
                return TDisconnect()
        # numbered control
        if control_flags == 0b10:
            return TAck(sequence_number=sequence_number)
        if control_flags == 0b11:
            return TNak(sequence_number=sequence_number)

        raise ConversionError(f"Unknown TPCI {raw_tpci:#10b}.")
Esempio n. 10
0
 def from_knx(cls, raw: tuple[int, ...]) -> HVACOperationMode:
     """Parse/deserialize from KNX/IP raw data."""
     cls.test_bytesarray(raw)
     if raw[0] & 8 > 0:
         return HVACOperationMode.FROST_PROTECTION
     if raw[0] & 4 > 0:
         return HVACOperationMode.NIGHT
     if raw[0] & 2 > 0:
         return HVACOperationMode.STANDBY
     if raw[0] & 1 > 0:
         return HVACOperationMode.COMFORT
     raise ConversionError(f"Payload not supported for {cls.__name__}", raw=raw)
Esempio n. 11
0
    def to_knx(cls, value):
        """Serialize to KNX/IP raw data."""
        try:
            percent_value = float(value)
            if not cls._test_boundaries(percent_value):
                raise ValueError
            delta = cls.value_max - cls.value_min
            knx_value = round((percent_value-cls.value_min)/delta*255)

            return (knx_value,)
        except ValueError:
            raise ConversionError("Cant serialize %s" % cls.__name__, value=value)
Esempio n. 12
0
    def from_knx(cls, raw):
        """Parse/deserialize from KNX/IP raw data."""
        cls.test_bytesarray(raw, 1)

        knx_value = raw[0]
        delta = cls.value_max - cls.value_min
        value = round((knx_value/255)*delta) + cls.value_min

        if not cls._test_boundaries(value):
            raise ConversionError("Cant parse %s" % cls.__name__, value=value, raw=raw)

        return value
Esempio n. 13
0
 def to_knx(self, value: "RemoteValueUpDown.Direction") -> DPTBinary:
     """Convert value to payload."""
     if value == self.Direction.UP:
         return DPTBinary(1) if self.invert else DPTBinary(0)
     if value == self.Direction.DOWN:
         return DPTBinary(0) if self.invert else DPTBinary(1)
     raise ConversionError(
         "value invalid",
         value=value,
         device_name=self.device_name,
         feature_name=self.feature_name,
     )
Esempio n. 14
0
    def to_knx(self) -> list[int]:
        """Serialize to KNX/IP raw data."""
        if not isinstance(self.payload, APCI):
            raise TypeError()
        if not isinstance(self.src_addr, (GroupAddress, IndividualAddress)):
            raise ConversionError("src_addr not set")
        if not isinstance(self.dst_addr, (GroupAddress, IndividualAddress)):
            raise ConversionError("dst_addr not set")

        data = []

        data.append(self.code.value)
        data.append(0x00)
        data.append((self.flags >> 8) & 255)
        data.append(self.flags & 255)
        data.extend(self.src_addr.to_knx())
        data.extend(self.dst_addr.to_knx())
        data.append(self.payload.calculated_length())
        data.extend(self.payload.to_knx())

        return data
Esempio n. 15
0
    def from_knx(cls, raw):
        """Parse/deserialize from KNX/IP raw data."""
        cls.test_bytesarray(raw, 1)

        value = raw[0]

        if not cls._test_boundaries(value):
            raise ConversionError("Cant parse DPTSceneNumber",
                                  value=value,
                                  raw=raw)

        return value
Esempio n. 16
0
    def from_knx(cls, raw):
        """Parse/deserialize from KNX/IP raw data."""
        cls.test_bytesarray(raw)

        value = raw[0]

        if not cls._test_boundaries(value):
            raise ConversionError(
                "Could not parse %s" % cls.__name__, value=value, raw=raw
            )

        return value
Esempio n. 17
0
 def to_knx(self, value):
     """Convert value to payload."""
     if value == self.Direction.INCREASE:
         return DPTBinary(0) if self.invert else DPTBinary(1)
     if value == self.Direction.DECREASE:
         return DPTBinary(1) if self.invert else DPTBinary(0)
     raise ConversionError(
         "value invalid",
         value=value,
         device_name=self.device_name,
         feature_name=self.feature_name,
     )
Esempio n. 18
0
    def to_knx(self, value):
        """Convert value to payload."""
        if not isinstance(value, (list, tuple)):
            raise ConversionError(
                "Could not serialize RemoteValueColorRGB (wrong type)",
                value=value,
                type=type(value),
            )
        if len(value) != 3:
            raise ConversionError(
                "Could not serialize DPT 232.600 (wrong length)",
                value=value,
                type=type(value),
            )
        if (any(not isinstance(color, int) for color in value)
                or any(color < 0 for color in value)
                or any(color > 255 for color in value)):
            raise ConversionError(
                "Could not serialize DPT 232.600 (wrong bytes)", value=value)

        return DPTArray(list(value))
Esempio n. 19
0
    def from_knx(cls, raw: tuple[int, ...]) -> int:
        """Parse/deserialize from KNX/IP raw data."""
        cls.test_bytesarray(raw)

        value = raw[0]

        if not cls._test_boundaries(value):
            raise ConversionError(f"Could not parse {cls.__name__}",
                                  value=value,
                                  raw=raw)

        return value
Esempio n. 20
0
 def to_knx(cls, value):
     """Serialize to KNX/IP raw data."""
     try:
         knx_value = int(value)
         if not cls._test_boundaries(knx_value):
             raise ValueError
         if knx_value < 0:
             knx_value += 0x100
         return (knx_value & 0xff, )
     except ValueError:
         raise ConversionError("Cant serialize %s" % cls.__name__,
                               value=value)
Esempio n. 21
0
    def from_knx(cls, raw: Tuple[int, ...]) -> time.struct_time:
        """Parse/deserialize from KNX/IP raw data."""
        cls.test_bytesarray(raw)

        day = raw[0] & 0x1F
        month = raw[1] & 0x0F
        year = raw[2] & 0x7F

        if not DPTDate._test_range(day, month, year):
            raise ConversionError("Could not parse DPTDate", raw=raw)

        if year >= 90:
            year += 1900
        else:
            year += 2000

        try:
            # strptime conversion used for catching exceptions; filled with default values
            return time.strptime(f"{year} {month} {day}", "%Y %m %d")
        except ValueError:
            raise ConversionError("Could not parse DPTDate", raw=raw)
Esempio n. 22
0
    def to_knx(self, value):
        """
        Convert value (4-6 bytes) to payload (6 bytes).

        * Structure of DPT 251.600
        ** Byte 0: R value
        ** Byte 1: G value
        ** Byte 2: B value
        ** Byte 3: W value
        ** Byte 4: 0x00 (reserved)
        ** Byte 5:
        *** Bit 0: W value valid?
        *** Bit 1: B value valid?
        *** Bit 2: G value valid?
        *** Bit 3: R value valid?
        *** Bit 4-7: 0

        In case we receive
        * > 6 bytes: error
        * 6 bytes: all bytes are passed through
        * 5 bytes: 0x00?? fill up to 6 bytes
        * 4 bytes: 0x000f right padding to 6 bytes
        * < 4 bytes: error
        """
        if not isinstance(value, (list, tuple)):
            raise ConversionError("Cannot serialize RemoteValueColorRGBW (wrong type, expecting list of 4-6 bytes))",
                                  value=value, type=type(value))
        if len(value) < 4 or len(value) > 6:
            raise ConversionError("Cannot serialize value to DPT 251.600 (wrong length, expecting list of 4-6 bytes)",
                                  value=value, type=type(value))
        rgbw = value[:4]
        if any(not isinstance(color, int) for color in rgbw) \
                or any(color < 0 for color in rgbw) \
                or any(color > 255 for color in rgbw):
            raise ConversionError("Cannot serialize DPT 251.600 (wrong RGBW values)", value=value)
        if len(value) < 5:
            return DPTArray(list(rgbw) + [0x00, 0x0f])
        if len(value) < 6:
            return DPTArray(list(rgbw) + [0x00] + list(value[4:]))
        return DPTArray(value)
Esempio n. 23
0
    def to_knx(cls, value: float) -> tuple[int]:
        """Serialize to KNX/IP raw data."""
        try:
            percent_value = float(value)
            if not cls._test_boundaries(percent_value):
                raise ValueError
            delta = cls.value_max - cls.value_min
            knx_value = round((percent_value - cls.value_min) / delta * 255)

            return (knx_value, )
        except ValueError:
            raise ConversionError(f"Could not serialize {cls.__name__}",
                                  value=value)
Esempio n. 24
0
 def to_knx(cls, value):
     """Serialize to KNX/IP raw data."""
     try:
         knx_value = str(value)
         if not cls._test_boundaries(knx_value):
             raise ValueError
         raw = []
         for character in knx_value:
             raw.append(ord(character))
         raw.extend([0] * (cls.payload_length - len(raw)))
         return raw
     except ValueError:
         raise ConversionError("Cant serialize %s" % cls.__name__, value=value)
Esempio n. 25
0
    def to_knx(cls, value: Any) -> tuple[int]:
        """Serialize to KNX/IP raw data."""
        # TODO: use Tuple or Named Tuple instead of Dict[str, int] to account for bool control
        if not isinstance(value, dict):
            raise ConversionError(
                "Cant serialize %s; invalid value type" % cls.__name__, value=value
            )

        try:
            control = bool(value["control"])
            step_code = value["step_code"]
        except KeyError:
            raise ConversionError(
                "Cant serialize %s; invalid keys" % cls.__name__, value=value
            )

        if not cls._test_values(step_code):
            raise ConversionError(
                "Cant serialize %s; invalid values" % cls.__name__, value=value
            )

        return (cls._encode(control, step_code),)
Esempio n. 26
0
 def to_knx(cls, value):
     """Serialize to KNX/IP raw data."""
     if value == HVACOperationMode.AUTO:
         return (0, )
     if value == HVACOperationMode.COMFORT:
         return (1, )
     if value == HVACOperationMode.STANDBY:
         return (2, )
     if value == HVACOperationMode.NIGHT:
         return (3, )
     if value == HVACOperationMode.FROST_PROTECTION:
         return (4, )
     raise ConversionError("Could not parse HVACOperationMode", value=value)
Esempio n. 27
0
 def to_knx(cls, value: str) -> tuple[int, ...]:
     """Serialize to KNX/IP raw data."""
     try:
         knx_value = str(value)
         if not cls._test_boundaries(knx_value):
             raise ValueError
     except ValueError:
         raise ConversionError(f"Could not serialize {cls.__name__}",
                               value=value)
     # replace invalid characters with question marks
     raw_bytes = knx_value.encode(cls._encoding, errors="replace")
     padding = bytes(cls.payload_length - len(raw_bytes))
     return tuple(raw_bytes + padding)
Esempio n. 28
0
    def to_knx(cls, value: time.struct_time) -> tuple[int, int, int]:
        """Serialize to KNX/IP raw data from time.struct_time."""
        def _knx_year(year: int) -> int:
            if 2000 <= year < 2090:
                return year - 2000
            if 1990 <= year < 2000:
                return year - 1900
            raise ConversionError("Could not serialize DPTDate", year=year)

        if not isinstance(value, time.struct_time):
            raise ConversionError("Could not serialize DPTDate", value=value)

        return (value.tm_mday, value.tm_mon, _knx_year(value.tm_year))
Esempio n. 29
0
 def to_knx(cls, value):
     """Serialize to KNX/IP raw data."""
     if value == HVACOperationMode.AUTO:
         from xknx.exceptions import ConversionError
         raise ConversionError(value)
     elif value == HVACOperationMode.COMFORT:
         return (0x21, )
     elif value == HVACOperationMode.STANDBY:
         return (0x22, )
     elif value == HVACOperationMode.NIGHT:
         return (0x24, )
     elif value == HVACOperationMode.FROST_PROTECTION:
         return (0x28, )
Esempio n. 30
0
    def to_knx(self) -> bytes:
        """Serialize to KNX/IP raw data."""
        if self.count < 0 or self.count > 2**4:
            raise ConversionError("Count out of range.")

        payload = struct.pack(
            "!BBBB",
            self.object_index,
            self.property_id,
            self.count << 4,
            self.start_index,
        )

        return encode_cmd_and_payload(self.CODE, appended_payload=payload)