示例#1
0
def on_io_sample_received(sample: IOSample, remote: RemoteXBeeDevice,
                          time: int):

    mutex.acquire()

    # get the address of the sender node
    addr = str(remote.get_64bit_addr())

    print("*****************************************************")
    print("IO Sample Received from: " + str(addr))
    print("FRAME ID: " + str(remote.get_current_frame_id()))

    # add this address to our node data if it doesn't exist
    if addr not in NODE_DATA:
        NODE_DATA[addr] = {}

    # Data we have about our configured nodes
    nodes = SPACE_CONFIGURATION['nodes']

    if str(addr) not in nodes:
        #print (f'addr {addr} not in configuration file!!')
        return

    # Data we have about the node that triggered this callback
    node = nodes[str(addr)]
    # The name of this garage
    location = SPACE_CONFIGURATION['location']

    # go through each dio and see if the incoming IOSample has it and what it's value is
    for s in node:
        config_dio = s['dio']
        space = int(s['space'])

        # unpack useful configuration data
        dio = DIO_LINES[config_dio]

        # this io sample is not present in this update, just ignore and continue
        if not sample.has_digital_value(dio):
            continue

        # Get the current known state of this dio line
        spot = AWS.get_spot(location, space)

        if 'Item' not in spot or 'Occupied' not in spot['Item']:
            current = None
        else:
            current = spot['Item']['Occupied']

        # Get the new state of this dio line
        new = sample.get_digital_value(dio) == IOValue.LOW

        # if they are the same, just continue
        if new == current:
            #print (f"Incoming dio line {dio} from addr {addr} is the same!")
            continue

        # update our internal map of data
        NODE_DATA[addr][dio] = new

        AWS.set_spot(location, space, new)

    print("*****************************************************")

    mutex.release()
示例#2
0
class IODataSampleRxIndicatorWifiPacket(XBeeAPIPacket):
    """
    This class represents a IO data sample RX indicator (Wi-Fi) packet. Packet
    is built using the parameters of the constructor or providing a valid API
    payload.

    When the module receives an IO sample frame from a remote device, it sends
    the sample out the UART or SPI using this frame type. Only modules running
    API mode will be able to receive IO samples.

    Among received data, some options can also be received indicating
    transmission parameters.

    .. seealso::
       | :class:`.XBeeAPIPacket`
    """

    __MIN_PACKET_LENGTH = 16

    def __init__(self,
                 src_address,
                 rssi,
                 rx_options,
                 rf_data=None,
                 op_mode=OperatingMode.API_MODE):
        """
        Class constructor. Instantiates a new
        :class:`.IODataSampleRxIndicatorWifiPacket` object with the
        provided parameters.

        Args:
            src_address (:class:`ipaddress.IPv4Address`): the 64-bit source address.
            rssi (Integer): received signal strength indicator.
            rx_options (Integer): bitfield indicating the receive options.
            rf_data (Bytearray, optional): received RF data.
            op_mode (:class:`.OperatingMode`, optional, default=`OperatingMode.API_MODE`):
                The mode in which the frame was captured.

        Raises:
            ValueError: if `rf_data` is not `None` and it's not valid for
                create an :class:`.IOSample`.

        .. seealso::
           | :class:`.IOSample`
           | :class:`ipaddress.IPv4Address`
           | :class:`.ReceiveOptions`
           | :class:`.XBeeAPIPacket`
        """
        super().__init__(ApiFrameType.IO_DATA_SAMPLE_RX_INDICATOR_WIFI,
                         op_mode=op_mode)
        self.__src_addr = src_address
        self.__rssi = rssi
        self.__rx_opts = rx_options
        self.__data = rf_data
        self.__io_sample = IOSample(
            rf_data) if rf_data is not None and len(rf_data) >= 5 else None

    @staticmethod
    def create_packet(raw, operating_mode):
        """
        Override method.

        Returns:
            :class:`.IODataSampleRxIndicatorWifiPacket`.

        Raises:
            InvalidPacketException: if the bytearray length is less than 16.
                (start delim. + length (2 bytes) + frame type
                + source addr. (4 bytes) + rssi + receive options
                + rf data (5 bytes) + checksum = 16 bytes).
            InvalidPacketException: if the length field of 'raw' is different
                from its real length. (length field: bytes 2 and 3)
            InvalidPacketException: if the first byte of 'raw' is not the
                header byte. See :class:`.SpecialByte`.
            InvalidPacketException: if the calculated checksum is different
                from the checksum field value (last byte).
            InvalidPacketException: if the frame type is not
                :attr:`.ApiFrameType.IO_DATA_SAMPLE_RX_INDICATOR_WIFI`.
            InvalidOperatingModeException: if `operating_mode` is not supported.

        .. seealso::
           | :meth:`.XBeePacket.create_packet`
           | :meth:`.XBeeAPIPacket._check_api_packet`
        """
        if operating_mode not in (OperatingMode.ESCAPED_API_MODE,
                                  OperatingMode.API_MODE):
            raise InvalidOperatingModeException(op_mode=operating_mode)

        XBeeAPIPacket._check_api_packet(
            raw,
            min_length=IODataSampleRxIndicatorWifiPacket.__MIN_PACKET_LENGTH)

        if raw[3] != ApiFrameType.IO_DATA_SAMPLE_RX_INDICATOR_WIFI.code:
            raise InvalidPacketException(
                message=
                "This packet is not an IO data sample RX indicator Wi-Fi packet."
            )

        return IODataSampleRxIndicatorWifiPacket(IPv4Address(bytes(raw[4:8])),
                                                 raw[7],
                                                 raw[8],
                                                 rf_data=raw[9:-1],
                                                 op_mode=operating_mode)

    def needs_id(self):
        """
        Override method.

        .. seealso::
           | :meth:`.XBeeAPIPacket.needs_id`
        """
        return False

    def _get_api_packet_spec_data(self):
        """
        Override method.

        .. seealso::
           | :meth:`.XBeeAPIPacket._get_api_packet_spec_data`
        """
        ret = bytearray(self.__src_addr.packed)
        ret += utils.int_to_bytes(self.__rssi, num_bytes=1)
        ret += utils.int_to_bytes(self.__rx_opts, num_bytes=1)
        if self.__data is not None:
            ret += self.__data
        return ret

    def _get_api_packet_spec_data_dict(self):
        """
        Override method.

        .. seealso::
           | :meth:`.XBeeAPIPacket._get_api_packet_spec_data_dict`
        """
        base = {
            DictKeys.SRC_IPV4_ADDR:
            "%s (%s)" % (self.__src_addr.packed, self.__src_addr.exploded),
            DictKeys.RSSI:
            self.__rssi,
            DictKeys.RECEIVE_OPTIONS:
            self.__rx_opts
        }

        if self.__io_sample is not None:
            base[DictKeys.NUM_SAMPLES] = 1
            base[DictKeys.DIGITAL_MASK] = self.__io_sample.digital_mask
            base[DictKeys.ANALOG_MASK] = self.__io_sample.analog_mask

            # Digital values
            for i in range(16):
                if self.__io_sample.has_digital_value(IOLine.get(i)):
                    base[IOLine.get(i).description + " digital value"] = \
                        self.__io_sample.get_digital_value(IOLine.get(i)).name

            # Analog values
            for i in range(6):
                if self.__io_sample.has_analog_value(IOLine.get(i)):
                    base[IOLine.get(i).description + " analog value"] = \
                        self.__io_sample.get_analog_value(IOLine.get(i))

            # Power supply
            if self.__io_sample.has_power_supply_value():
                base[
                    "Power supply value "] = "%02X" % self.__io_sample.power_supply_value

        elif self.__data is not None:
            base[DictKeys.RF_DATA] = utils.hex_to_string(self.__data)

        return base

    @property
    def effective_len(self):
        """
        Override method.

        .. seealso::
           | :meth:`.XBeeAPIPacket.effective_len`
        """
        return len(self) - 8  # Remove 64-bit address

    @property
    def source_address(self):
        """
        Returns the IPv4 address of the source device.

        Returns:
            :class:`ipaddress.IPv4Address`: the IPv4 address of the source device.

        .. seealso::
           | :class:`ipaddress.IPv4Address`
        """
        return self.__src_addr

    @source_address.setter
    def source_address(self, source_address):
        """
        Sets the IPv4 source address.

        Args:
            source_address (:class:`ipaddress.IPv4Address`): The new IPv4 source address.

        .. seealso::
           | :class:`ipaddress.IPv4Address`
        """
        if source_address is not None:
            self.__src_addr = source_address

    @property
    def rssi(self):
        """
        Returns the received Signal Strength Indicator (RSSI).

        Returns:
            Integer: the received Signal Strength Indicator (RSSI).
        """
        return self.__rssi

    @rssi.setter
    def rssi(self, rssi):
        """
        Sets the received Signal Strength Indicator (RSSI).

        Args:
            rssi (Integer): the new received Signal Strength Indicator (RSSI).
        """
        self.__rssi = rssi

    @property
    def receive_options(self):
        """
        Returns the receive options bitfield.

        Returns:
            Integer: the receive options bitfield.

        .. seealso::
           | :class:`.ReceiveOptions`
        """
        return self.__rx_opts

    @receive_options.setter
    def receive_options(self, receive_options):
        """
        Sets the receive options bitfield.

        Args:
            receive_options (Integer): the new receive options bitfield.

        .. seealso::
           | :class:`.ReceiveOptions`
        """
        self.__rx_opts = receive_options

    @property
    def rf_data(self):
        """
        Returns the received RF data.

        Returns:
            Bytearray: the received RF data.
        """
        if self.__data is None:
            return None
        return self.__data.copy()

    @rf_data.setter
    def rf_data(self, rf_data):
        """
        Sets the received RF data.

        Args:
            rf_data (Bytearray): the new received RF data.
        """
        if rf_data is None:
            self.__data = None
        else:
            self.__data = rf_data.copy()

        # Modify the IO sample accordingly
        if rf_data is not None and len(rf_data) >= 5:
            self.__io_sample = IOSample(self.__data)
        else:
            self.__io_sample = None

    @property
    def io_sample(self):
        """
        Returns the IO sample corresponding to the data contained in the packet.

        Returns:
            :class:`.IOSample`: the IO sample of the packet, `None` if the
                packet has not any data or if the sample could not be
                generated correctly.

        .. seealso::
           | :class:`.IOSample`
        """
        return self.__io_sample

    @io_sample.setter
    def io_sample(self, io_sample):
        """
        Sets the IO sample of the packet.

        Args:
            io_sample (:class:`.IOSample`): the new IO sample to set.

        .. seealso::
           | :class:`.IOSample`
        """
        self.__io_sample = io_sample
示例#3
0
class RX64IOPacket(XBeeAPIPacket):
    """
    This class represents an RX64 address IO packet. Packet is built using the
    parameters of the constructor or providing a valid API payload.

    I/O data is sent out the UART using an API frame.

    .. seealso::
       | :class:`.XBeeAPIPacket`
    """

    __MIN_PACKET_LENGTH = 20

    def __init__(self, x64bit_addr, rssi, receive_options, rf_data):
        """
        Class constructor. Instantiates an :class:`.RX64IOPacket` object with the provided parameters.

        Args:
            x64bit_addr (:class:`.XBee64BitAddress`): the 64-bit source address.
            rssi (Integer): received signal strength indicator.
            receive_options (Integer): bitfield indicating the receive options.
            rf_data (Bytearray): received RF data.

        .. seealso::
           | :class:`.ReceiveOptions`
           | :class:`.XBee64BitAddress`
           | :class:`.XBeeAPIPacket`
        """
        super().__init__(ApiFrameType.RX_IO_64)
        self.__x64bit_addr = x64bit_addr
        self.__rssi = rssi
        self.__receive_options = receive_options
        self.__rf_data = rf_data
        self.__io_sample = IOSample(
            rf_data) if rf_data is not None and len(rf_data) >= 5 else None

    @staticmethod
    def create_packet(raw, operating_mode):
        """
        Override method.

        Returns:
            :class:`.RX64IOPacket`.

        Raises:
            InvalidPacketException: if the bytearray length is less than 20. (start delim. + length (2 bytes) + frame
                type + 64bit addr. + rssi + receive options + rf data (5 bytes) + checksum = 20 bytes)
            InvalidPacketException: if the length field of 'raw' is different than its real length. (length field: bytes
                2 and 3)
            InvalidPacketException: if the first byte of 'raw' is not the header byte. See :class:`.SpecialByte`.
            InvalidPacketException: if the calculated checksum is different than the checksum field value (last byte).
            InvalidPacketException: if the frame type is different than :attr:`.ApiFrameType.RX_IO_64`.
            InvalidOperatingModeException: if ``operating_mode`` is not supported.

        .. seealso::
           | :meth:`.XBeePacket.create_packet`
           | :meth:`.XBeeAPIPacket._check_api_packet`
        """
        if operating_mode != OperatingMode.ESCAPED_API_MODE and operating_mode != OperatingMode.API_MODE:
            raise InvalidOperatingModeException(operating_mode.name +
                                                " is not supported.")

        raw = XBeeAPIPacket._unescape_data(
            raw) if operating_mode == OperatingMode.ESCAPED_API_MODE else raw

        XBeeAPIPacket._check_api_packet(
            raw, min_length=RX64IOPacket.__MIN_PACKET_LENGTH)
        if raw[3] != ApiFrameType.RX_IO_64.code:
            raise InvalidPacketException(
                "This packet is not an RX 64 IO packet.")

        return RX64IOPacket(XBee64BitAddress(raw[4:12]), raw[12], raw[13],
                            raw[14:-1])

    def needs_id(self):
        """
        Override method.

        .. seealso::
           | :meth:`.XBeeAPIPacket.needs_id`
        """
        return False

    def _get_api_packet_spec_data(self):
        """
        Override method.

        .. seealso::
           | :meth:`.XBeeAPIPacket._get_api_packet_spec_data`
        """
        ret = self.__x64bit_addr.address
        ret.append(self.__rssi)
        ret.append(self.__receive_options)
        if self.__rf_data is not None:
            ret += self.__rf_data
        return ret

    def _get_api_packet_spec_data_dict(self):
        """
        Override method.

        .. seealso::
           | :meth:`.XBeeAPIPacket._get_api_packet_spec_data_dict`
        """
        base = {
            DictKeys.X16BIT_ADDR: self.__x64bit_addr.address,
            DictKeys.RSSI: self.__rssi,
            DictKeys.RECEIVE_OPTIONS: self.__receive_options
        }

        if self.__io_sample is not None:
            base[DictKeys.NUM_SAMPLES] = 1
            base[DictKeys.DIGITAL_MASK] = self.__io_sample.digital_mask
            base[DictKeys.ANALOG_MASK] = self.__io_sample.analog_mask

            # Digital values
            for i in range(16):
                if self.__io_sample.has_digital_value(IOLine.get(i)):
                    base[IOLine.get(i).description + "digital value"] = \
                        utils.hex_to_string(self.__io_sample.get_digital_value(IOLine.get(i)))

            # Analog values
            for i in range(6):
                if self.__io_sample.has_analog_value(IOLine.get(i)):
                    base[IOLine.get(i).description + "analog value"] = \
                        utils.hex_to_string(self.__io_sample.get_analog_value(IOLine.get(i)))

            # Power supply
            if self.__io_sample.has_power_supply_value():
                base[
                    "Power supply value "] = "%02X" % self.__io_sample.power_supply_value

        elif self.__rf_data is not None:
            base[DictKeys.RF_DATA] = utils.hex_to_string(self.__rf_data)

        return base

    def __get_64bit_addr(self):
        """
        Returns the 64-bit source address.

        Returns:
            :class:`XBee64BitAddress`: the 64-bit source address.

        .. seealso::
           | :class:`.XBee64BitAddress`
        """
        return self.__x64bit_addr

    def __set_64bit_addr(self, x64bit_addr):
        """
        Sets the 64-bit source address.

        Args:
            x64bit_addr (:class:`.XBee64BitAddress`): the new 64-bit source address.

        .. seealso::
           | :class:`.XBee64BitAddress`
        """
        self.__x64bit_addr = x64bit_addr

    def __get_rssi(self):
        """
        Returns the received Signal Strength Indicator (RSSI).

        Returns:
            Integer: the received Signal Strength Indicator (RSSI).
        """
        return self.__rssi

    def __set_rssi(self, rssi):
        """
        Sets the received Signal Strength Indicator (RSSI).

        Args:
            rssi (Integer): the new received Signal Strength Indicator (RSSI).
        """
        self.__rssi = rssi

    def __get_options(self):
        """
        Returns the receive options bitfield.

        Returns:
            Integer: the receive options bitfield.

        .. seealso::
           | :class:`.ReceiveOptions`
        """
        return self.__receive_options

    def __set_options(self, receive_options):
        """
        Sets the receive options bitfield.

        Args:
            receive_options (Integer): the new receive options bitfield.

        .. seealso::
           | :class:`.ReceiveOptions`
        """
        self.__receive_options = receive_options

    def __get_rf_data(self):
        """
        Returns the received RF data.

        Returns:
            Bytearray: the received RF data.
        """
        if self.__rf_data is None:
            return None
        return self.__rf_data.copy()

    def __set_rf_data(self, rf_data):
        """
        Sets the received RF data.

        Args:
            rf_data (Bytearray): the new received RF data.
        """
        if rf_data is None:
            self.__rf_data = None
        else:
            self.__rf_data = rf_data.copy()

        # Modify the ioSample accordingly
        if rf_data is not None and len(rf_data) >= 5:
            self.__io_sample = IOSample(self.__rf_data)
        else:
            self.__io_sample = None

    def __get_io_sample(self):
        """
        Returns the IO sample corresponding to the data contained in the packet.

        Returns:
            :class:`.IOSample`: the IO sample of the packet, ``None`` if the packet has not any data or if the
                sample could not be generated correctly.

        .. seealso::
           | :class:`.IOSample`
        """
        return self.__io_sample

    def __set_io_sample(self, io_sample):
        """
        Sets the IO sample of the packet.

        Args:
            io_sample (:class:`.IOSample`): the new IO sample to set.

        .. seealso::
           | :class:`.IOSample`
        """
        self.__io_sample = io_sample

    x64bit_source_addr = property(__get_64bit_addr, __set_64bit_addr)
    """:class:`.XBee64BitAddress`. 64-bit source address."""

    rssi = property(__get_rssi, __set_rssi)
    """Integer. Received Signal Strength Indicator (RSSI) value."""

    receive_options = property(__get_options, __set_options)
    """Integer. Receive options bitfield."""

    rf_data = property(__get_rf_data, __set_rf_data)
    """Bytearray. Received RF data."""

    io_sample = property(__get_io_sample, __set_io_sample)
    """:class:`.IOSample`: IO sample corresponding to the data contained in the packet."""
class RX16IOPacket(XBeeAPIPacket):
    """
    This class represents an RX16 address IO packet. Packet is built using the
    parameters of the constructor or providing a valid byte array.

    I/O data is sent out the UART using an API frame.

    .. seealso::
       | :class:`.XBeeAPIPacket`
    """

    __MIN_PACKET_LENGTH = 14

    def __init__(self, x16bit_addr, rssi, receive_options, rf_data):
        """
        Class constructor. Instantiates an :class:`.RX16IOPacket` object with
        the provided parameters.

        Args:
            x16bit_addr (:class:`.XBee16BitAddress`): the 16-bit source address.
            rssi (Integer): received signal strength indicator.
            receive_options (Integer): bitfield indicating the receive options.
            rf_data (Bytearray): received RF data.

        .. seealso::
           | :class:`.ReceiveOptions`
           | :class:`.XBee16BitAddress`
           | :class:`.XBeeAPIPacket`
        """
        super().__init__(ApiFrameType.RX_IO_16)
        self.__x16bit_addr = x16bit_addr
        self.__rssi = rssi
        self.__receive_options = receive_options
        self.__rf_data = rf_data
        self.__io_sample = IOSample(
            rf_data) if rf_data is not None and len(rf_data) >= 5 else None

    @staticmethod
    def create_packet(raw, operating_mode):
        """
        Override method.

        Returns:
            :class:`.RX16IOPacket`.

        Raises:
            InvalidPacketException: if the bytearray length is less than 14.
                (start delim. + length (2 bytes) + frame type + 16bit addr.
                + rssi + receive options + rf data (5 bytes) + checksum = 14 bytes).
            InvalidPacketException: if the length field of 'raw' is different
                from its real length. (length field: bytes 2 and 3)
            InvalidPacketException: if the first byte of 'raw' is not the
                header byte. See :class:`.SpecialByte`.
            InvalidPacketException: if the calculated checksum is different
                from the checksum field value (last byte).
            InvalidPacketException: if the frame type is different from
                :attr:`.ApiFrameType.RX_IO_16`.
            InvalidOperatingModeException: if `operating_mode` is not supported.

        .. seealso::
           | :meth:`.XBeePacket.create_packet`
           | :meth:`.XBeeAPIPacket._check_api_packet`
        """
        if operating_mode not in (OperatingMode.ESCAPED_API_MODE,
                                  OperatingMode.API_MODE):
            raise InvalidOperatingModeException(op_mode=operating_mode)

        XBeeAPIPacket._check_api_packet(
            raw, min_length=RX16IOPacket.__MIN_PACKET_LENGTH)

        if raw[3] != ApiFrameType.RX_IO_16.code:
            raise InvalidPacketException(
                message="This packet is not an RX 16 IO packet.")

        return RX16IOPacket(XBee16BitAddress(raw[4:6]), raw[6], raw[7],
                            raw[8:-1])

    def needs_id(self):
        """
        Override method.

        .. seealso::
           | :meth:`.XBeeAPIPacket.needs_id`
        """
        return False

    def is_broadcast(self):
        """
        Override method.

        .. seealso::
           | :meth:`XBeeAPIPacket.is_broadcast`
        """
        return (utils.is_bit_enabled(self.__receive_options, 1)
                or utils.is_bit_enabled(self.__receive_options, 2))

    def _get_api_packet_spec_data(self):
        """
        Override method.

        .. seealso::
           | :meth:`.XBeeAPIPacket._get_api_packet_spec_data`
        """
        ret = self.__x16bit_addr.address
        ret.append(self.__rssi)
        ret.append(self.__receive_options)
        if self.__rf_data is not None:
            ret += self.__rf_data
        return ret

    def _get_api_packet_spec_data_dict(self):
        """
        Override method.

        .. seealso::
           | :meth:`.XBeeAPIPacket._get_api_packet_spec_data_dict`
        """
        base = {
            DictKeys.X16BIT_ADDR: self.__x16bit_addr.address,
            DictKeys.RSSI: self.__rssi,
            DictKeys.RECEIVE_OPTIONS: self.__receive_options
        }

        if self.__io_sample is not None:
            base[DictKeys.NUM_SAMPLES] = 1
            base[DictKeys.DIGITAL_MASK] = self.__io_sample.digital_mask
            base[DictKeys.ANALOG_MASK] = self.__io_sample.analog_mask

            # Digital values
            for i in range(16):
                if self.__io_sample.has_digital_value(IOLine.get(i)):
                    base[IOLine.get(i).description + "digital value"] = \
                        utils.hex_to_string(self.__io_sample.get_digital_value(IOLine.get(i)))

            # Analog values
            for i in range(6):
                if self.__io_sample.has_analog_value(IOLine.get(i)):
                    base[IOLine.get(i).description + "analog value"] = \
                        utils.hex_to_string(self.__io_sample.get_analog_value(IOLine.get(i)))

            # Power supply
            if self.__io_sample.has_power_supply_value():
                base[
                    "Power supply value "] = "%02X" % self.__io_sample.power_supply_value

        elif self.__rf_data is not None:
            base[DictKeys.RF_DATA] = utils.hex_to_string(self.__rf_data)

        return base

    @property
    def x16bit_source_addr(self):
        """
        Returns the 16-bit source address.

        Returns:
            :class:`.XBee16BitAddress`: the 16-bit source address.

        .. seealso::
           | :class:`.XBee16BitAddress`
        """
        return self.__x16bit_addr

    @x16bit_source_addr.setter
    def x16bit_source_addr(self, x16bit_addr):
        """
        Sets the 16-bit source address.

        Args:
            x16bit_addr (:class:`.XBee16BitAddress`): the new 16-bit source address.

        .. seealso::
           | :class:`.XBee16BitAddress`
        """
        self.__x16bit_addr = x16bit_addr

    @property
    def rssi(self):
        """
        Returns the received Signal Strength Indicator (RSSI).

        Returns:
            Integer: the received Signal Strength Indicator (RSSI).
        """
        return self.__rssi

    @rssi.setter
    def rssi(self, rssi):
        """
        Sets the received Signal Strength Indicator (RSSI).

        Args:
            rssi (Integer): the new received Signal Strength Indicator (RSSI).

        """
        self.__rssi = rssi

    @property
    def receive_options(self):
        """
        Returns the receive options bitfield.

        Returns:
            Integer: the receive options bitfield.

        .. seealso::
           | :class:`.ReceiveOptions`
        """
        return self.__receive_options

    @receive_options.setter
    def receive_options(self, receive_options):
        """
        Sets the receive options bitfield.

        Args:
            receive_options (Integer): the new receive options bitfield.

        .. seealso::
           | :class:`.ReceiveOptions`
        """
        self.__receive_options = receive_options

    @property
    def rf_data(self):
        """
        Returns the received RF data.

        Returns:
            Bytearray: the received RF data.
        """
        if self.__rf_data is None:
            return None
        return self.__rf_data.copy()

    @rf_data.setter
    def rf_data(self, rf_data):
        """
        Sets the received RF data.

        Args:
            rf_data (Bytearray): the new received RF data.
        """
        if rf_data is None:
            self.__rf_data = None
        else:
            self.__rf_data = rf_data.copy()

        # Modify the ioSample accordingly
        if rf_data is not None and len(rf_data) >= 5:
            self.__io_sample = IOSample(self.__rf_data)
        else:
            self.__io_sample = None

    @property
    def io_sample(self):
        """
        Returns the IO sample corresponding to the data contained in the packet.

        Returns:
            :class:`.IOSample`: the IO sample of the packet, `None` if the
                packet has not any data or if the sample could not be generated
                correctly.

        .. seealso::
           | :class:`.IOSample`
        """
        return self.__io_sample

    @io_sample.setter
    def io_sample(self, io_sample):
        """
        Sets the IO sample of the packet.

        Args:
            io_sample (:class:`.IOSample`): the new IO sample to set.

        .. seealso::
           | :class:`.IOSample`
        """
        self.__io_sample = io_sample