From c86fbe8d1756963be97bed15c157954492d76de6 Mon Sep 17 00:00:00 2001 From: Diego Escalona Date: Thu, 18 Jan 2018 17:47:58 +0100 Subject: [PATCH 1/2] version: update the version to 1.1.0 - Added the new version entry to the 'CHANGELOG.rst' document. Signed-off-by: Diego Escalona --- CHANGELOG.rst | 16 +++++++++++++++- doc/conf.py | 4 ++-- setup.py | 4 ++-- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 705ae45..7d19f3a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,20 @@ Changelog ========= +v1.1.0 - 01/19/2018 +------------------- + +* Add support for new hardware variants: + * XB8X +* Add missing 'Modem Status' values for Remote Manager connect and disconnect + events. +* Bug Fixing: + * Fix timeouts on Unix plaforms. + * Fix the return source endpoint method from the 'ExplicitRXIndicatorPacket' + class. + * Perform general bug fixing when working in API escaped mode. + + v1.0.0 - 10/02/2017 ------------------- @@ -34,4 +48,4 @@ include: * Receive IO data samples from any remote XBee device on the network. * Support for explicit frames and application layer fields (Source endpoint, Destination endpoint, Profile ID, and Cluster ID). -* Multiple examples that show how to use the available APIs. \ No newline at end of file +* Multiple examples that show how to use the available APIs. diff --git a/doc/conf.py b/doc/conf.py index 3305467..f571375 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -56,9 +56,9 @@ # built documents. # # The short X.Y version. -version = '1.0.0' +version = '1.1.0' # The full version, including alpha/beta/rc tags. -release = '1.0.0' +release = '1.1.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/setup.py b/setup.py index 9d22819..118c2e4 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # -# Copyright (c) 2017 Digi International Inc. All Rights Reserved. +# Copyright (c) 2017, 2018, Digi International Inc. All Rights Reserved. from setuptools import setup, find_packages from codecs import open @@ -20,7 +20,7 @@ setup( name='digi-xbee', - version='1.0.0', + version='1.1.0', description='Digi XBee Python library', long_description=long_description, url='https://github.com/digidotcom/python-xbee', From 75678d7f84a57fa9294e567ea645d433fe6658ad Mon Sep 17 00:00:00 2001 From: Jordan Gassaway Date: Fri, 19 Jan 2018 14:14:02 -0700 Subject: [PATCH 2/2] Fixed several grammar errors and clarified other documentation. Standardized an Enum class and the pan ID used in examples. --- digi/xbee/devices.py | 334 +++++++++--------- digi/xbee/io.py | 38 +- digi/xbee/packets/common.py | 2 + digi/xbee/serial.py | 11 +- doc/examples.rst | 8 +- ...tting_started_with_xbee_python_library.rst | 4 +- .../communicating_with_xbee_devices.rst | 2 +- .../handling_analog_and_digital_io_lines.rst | 10 +- doc/user_doc/xbee_terminology.rst | 10 +- 9 files changed, 213 insertions(+), 206 deletions(-) diff --git a/digi/xbee/devices.py b/digi/xbee/devices.py index 1fb508b..d8271bb 100755 --- a/digi/xbee/devices.py +++ b/digi/xbee/devices.py @@ -198,7 +198,7 @@ def set_parameter(self, parameter, value): This flags only works for volatile memory, if you want to save changed parameters in non-volatile memory, even for remote devices, - you must execute "WR" command by some of the 2 ways mentioned above. + you must execute "WR" command by one of the 2 ways mentioned above. Args: parameter (String): parameter to set. @@ -313,12 +313,12 @@ def read_device_info(self): orig_protocol = self.get_protocol() # Protocol: self._protocol = XBeeProtocol.determine_protocol(self._hardware_version.code, self._firmware_version) - + if orig_protocol is not None and orig_protocol != XBeeProtocol.UNKNOWN and orig_protocol != self._protocol: raise XBeeException("Error reading device information: " "Your module seems to be %s and NOT %s. " % (self.get_protocol(), orig_protocol) + "Check if you are using the appropriate device class.") - + # 64-bit address: sh = self.get_parameter("SH") sl = self.get_parameter("SL") @@ -857,11 +857,11 @@ def get_dio_value(self, io_line): def set_dio_value(self, io_line, io_value): """ Sets the digital value (high or low) to the provided IO line. - + Args: io_line (:class:`.IOLine`): the digital IO line to sets its value. io_value (:class:`.IOValue`): the IO value to set to the IO line. - + Raises: TimeoutException: if the response is not received before the read timeout expires. XBeeException: if the XBee device's serial port is closed. @@ -878,12 +878,12 @@ def set_dio_value(self, io_line, io_value): def set_dio_change_detection(self, io_lines_set): """ Sets the digital IO lines to be monitored and sampled whenever their status changes. - + A ``None`` set of lines disables this feature. - + Args: io_lines_set: set of :class:`.IOLine`. - + Raises: TimeoutException: if the response is not received before the read timeout expires. XBeeException: if the XBee device's serial port is closed. @@ -913,7 +913,7 @@ def get_api_output_mode(self): Returns: :class:`.APIOutputMode`: the API output mode of the XBee device. - + Raises: TimeoutException: if the response is not received before the read timeout expires. XBeeException: if the XBee device's serial port is closed. @@ -929,7 +929,7 @@ def get_api_output_mode(self): def set_api_output_mode(self, api_output_mode): """ Sets the API output mode of the XBee device. - + Args: api_output_mode (:class:`.APIOutputMode`): the new API output mode of the XBee device. @@ -985,7 +985,7 @@ def _force_disassociate(self): def _refresh_if_cached(self, parameter, value): """ Refreshes the proper cached parameter depending on ``parameter`` value. - + If ``parameter`` is not a cached parameter, this method does nothing. Args: @@ -1002,7 +1002,7 @@ def _refresh_if_cached(self, parameter, value): def _get_next_frame_id(self): """ Returns the next frame ID of the XBee device. - + Returns: Integer: The next frame ID of the XBee device. """ @@ -1023,7 +1023,7 @@ def dec_function(self, *args, **kwargs): raise XBeeException("XBee device's serial port closed.") if (self._operating_mode != OperatingMode.API_MODE and self._operating_mode != OperatingMode.ESCAPED_API_MODE): - raise InvalidOperatingModeException("Not supported operating mode: " + + raise InvalidOperatingModeException("Not supported operating mode: " + str(args[0].operating_mode.description)) return func(self, *args, **kwargs) return dec_function @@ -1044,13 +1044,13 @@ def dec_function(*args, **kwargs): def _get_packet_by_id(self, frame_id): """ Reads packets until there is one packet found with the provided frame ID. - + Args: frame_id (Integer): frame ID to use for. Must be between 0 and 255. - + Returns: :class:XBeePacket: the first XBee packet read whose frame ID matches the provided one. - + Raises: ValueError: if ``frame_id`` is less than 0 or greater than 255. TimeoutException: if there was not any XBee packet matching the provided frame ID that could be read. @@ -1068,7 +1068,7 @@ def _get_packet_by_id(self, frame_id): def __is_api_packet(xbee_packet): """ Determines whether the provided XBee packet is an API packet or not. - + Returns: Boolean: ``True`` if the provided XBee packet is an API packet (its frame type is inside :class:`.ApiFrameType` enum), ``False`` otherwise. @@ -1096,7 +1096,7 @@ def __get_log(self): class XBeeDevice(AbstractXBeeDevice): """ This class represents a non-remote generic XBee device. - + This class has fields that are events. Its recommended to use only the append() and remove() method on them, or -= and += operators. If you do something more with them, it's for your own risk. @@ -1143,7 +1143,7 @@ def __init__(self, port, baud_rate, data_bits=serial.EIGHTBITS, stop_bits=serial _sync_ops_timeout=AbstractXBeeDevice._DEFAULT_TIMEOUT_SYNC_OPERATIONS): """ Class constructor. Instantiates a new :class:`.XBeeDevice` with the provided parameters. - + Args: port (Integer or String): serial port identifier. Integer: number of XBee device, numbering starts at zero. @@ -1154,10 +1154,10 @@ def __init__(self, port, baud_rate, data_bits=serial.EIGHTBITS, stop_bits=serial parity (Character, default: :attr:`.serial.PARITY_NONE`): comm port parity. flow_control (Integer, default: :attr:`.FlowControl.NONE`): comm port flow control. _sync_ops_timeout (Integer, default: 3): comm port read timeout. - + Raises: All exceptions raised by PySerial's Serial class constructor. - + .. seealso:: | PySerial documentation: http://pyserial.sourceforge.net """ @@ -1198,7 +1198,7 @@ def __init__(self, port, baud_rate, data_bits=serial.EIGHTBITS, stop_bits=serial def create_xbee_device(cls, comm_port_data): """ Creates and returns an :class:`.XBeeDevice` from data of the port to which is connected. - + Args: comm_port_data (Dictionary): dictionary with all comm port data needed. The dictionary keys are: @@ -1230,7 +1230,7 @@ def create_xbee_device(cls, comm_port_data): def open(self): """ Opens the communication with the XBee device and loads some information about it. - + Raises: TimeoutException: if there is any problem with the communication. InvalidOperatingModeException: if the XBee device's operating mode is not API or ESCAPED API. This @@ -1269,7 +1269,7 @@ def open(self): def close(self): """ Closes the communication with the XBee device. - + This method guarantees that all threads running are stopped and the serial port is closed. """ @@ -1303,7 +1303,7 @@ def __get_serial_port(self): def get_parameter(self, param): """ Override. - + .. seealso:: | :meth:`.AbstractXBeeDevice.get_parameter` """ @@ -1318,7 +1318,7 @@ def get_parameter(self, param): def set_parameter(self, param, value): """ Override. - + See: :meth:`.AbstractXBeeDevice.set_parameter` """ @@ -1501,20 +1501,20 @@ def _send_data_16(self, x16addr, data, transmit_options=TransmitOptions.NONE.val def send_data(self, remote_xbee_device, data, transmit_options=TransmitOptions.NONE.value): """ Blocking method. This method sends data to a remote XBee device synchronously. - + This method will wait for the packet response. - + The default timeout for this method is :attr:`.XBeeDevice._DEFAULT_TIMEOUT_SYNC_OPERATIONS`. - + Args: remote_xbee_device (:class:`.RemoteXBeeDevice`): the remote XBee device to send data to. data (String or Bytearray): the raw data to send. transmit_options (Integer, optional): transmit options, bitfield of :class:`.TransmitOptions`. Default to ``TransmitOptions.NONE.value``. - + Returns: :class:`.XBeePacket` the response. - + Raises: ValueError: if ``remote_xbee_device`` is ``None``. TimeoutException: if this method can't read a response packet in @@ -1703,9 +1703,9 @@ def _send_data_async_16(self, x16addr, data, transmit_options=TransmitOptions.NO def send_data_async(self, remote_xbee_device, data, transmit_options=TransmitOptions.NONE.value): """ Non-blocking method. This method sends data to a remote XBee device. - + This method won't wait for the response. - + Args: remote_xbee_device (:class:`.RemoteXBeeDevice`): the remote XBee device to send data to. data (String or Bytearray): the raw data to send. @@ -1744,18 +1744,18 @@ def send_data_async(self, remote_xbee_device, data, transmit_options=TransmitOpt def send_data_broadcast(self, data, transmit_options=TransmitOptions.NONE.value): """ Sends the provided data to all the XBee nodes of the network (broadcast). - - This method blocks till a success or error transmit status arrives or + + This method blocks till a success or error transmit status arrives or the configured receive timeout expires. - + The received timeout is configured using the :meth:`.AbstractXBeeDevice.set_sync_ops_timeout` method and can be consulted with :meth:`.AbstractXBeeDevice.get_sync_ops_timeout` method. - + Args: data (String or Bytearray): data to send. transmit_options (Integer, optional): transmit options, bitfield of :class:`.TransmitOptions`. Default to ``TransmitOptions.NONE.value``. - + Raises: TimeoutException: if this method can't read a response packet in :attr:`.XBeeDevice._DEFAULT_TIMEOUT_SYNC_OPERATIONS` seconds. @@ -1772,7 +1772,7 @@ def read_data(self, timeout=None): If a ``timeout`` is specified, this method blocks until new data is received or the timeout expires, throwing in that case a :class:`.TimeoutException`. - + Args: timeout (Integer, optional): read timeout in seconds. If it's ``None``, this method is non-blocking and will return ``None`` if there is no data available. @@ -1823,12 +1823,12 @@ def read_data_from(self, remote_xbee_device, timeout=None): def has_packets(self): """ - Returns whether the XBee device's queue has packets or not. - This do not include explicit packets. - + Returns whether or not the XBee device's queue contains packets. + This does not include explicit packets. + Return: Boolean: ``True`` if this XBee device's queue has packets, ``False`` otherwise. - + .. seealso:: | :meth:`.XBeeDevice.has_explicit_packets` """ @@ -1836,12 +1836,12 @@ def has_packets(self): def has_explicit_packets(self): """ - Returns whether the XBee device's queue has explicit packets or not. - This do not include non-explicit packets. - + Returns whether or not the XBee device's queue contains explicit packets. + This does not include non-explicit packets. + Return: Boolean: ``True`` if this XBee device's queue has explicit packets, ``False`` otherwise. - + .. seealso:: | :meth:`.XBeeDevice.has_packets` """ @@ -1858,7 +1858,7 @@ def flush_queues(self): def reset(self): """ Override method. - + .. seealso:: | :meth:`.AbstractXBeeDevice.reset` """ @@ -2002,10 +2002,10 @@ def del_expl_data_received_callback(self, callback): def get_xbee_device_callbacks(self): """ Returns this XBee internal callbacks for process received packets. - + This method is called by the PacketListener associated with this XBee to get its callbacks. These callbacks will be executed before user callbacks. - + Returns: :class:`.PacketReceived` """ @@ -2055,7 +2055,7 @@ def io_sample_callback(received_packet): def __get_operating_mode(self): """ Returns this XBee device's operating mode. - + Returns: :class:`.OperatingMode`. This XBee device's operating mode. """ @@ -2064,7 +2064,7 @@ def __get_operating_mode(self): def is_open(self): """ Returns whether this XBee device is open or not. - + Returns: Boolean. ``True`` if this XBee device is open, ``False`` otherwise. """ @@ -2073,7 +2073,7 @@ def is_open(self): def is_remote(self): """ Override method. - + .. seealso:: | :meth:`.AbstractXBeeDevice.is_remote` """ @@ -2082,7 +2082,7 @@ def is_remote(self): def get_network(self): """ Returns this XBee device's current network. - + Returns: :class:`.XBeeDevice.XBeeNetwork` """ @@ -2096,12 +2096,12 @@ def _send_expl_data(self, remote_xbee_device, data, src_endpoint, dest_endpoint, Blocking method. Sends the provided data to the given XBee device in application layer mode. Application layer mode means that you need to specify the application layer fields to be sent with the data. - + This method blocks till a success or error response arrives or the configured receive timeout expires. - + The default timeout for this method is :attr:`.XBeeDevice._DEFAULT_TIMEOUT_SYNC_OPERATIONS`. - + Args: remote_xbee_device (:class:`.RemoteXBeeDevice`): the remote XBee device to send data to. data (String or Bytearray): the raw data to send. @@ -2141,7 +2141,7 @@ def _send_expl_data_async(self, remote_xbee_device, data, src_endpoint, dest_end Non-blocking method. Sends the provided data to the given XBee device in application layer mode. Application layer mode means that you need to specify the application layer fields to be sent with the data. - + Args: remote_xbee_device (:class:`.RemoteXBeeDevice`): the remote XBee device to send data to. data (String or Bytearray): the raw data to send. @@ -2151,7 +2151,7 @@ def _send_expl_data_async(self, remote_xbee_device, data, src_endpoint, dest_end profile_id (Integer): Profile ID of the transmission. Must be between 0x0 and 0xFFFF. transmit_options (Integer, optional): transmit options, bitfield of :class:`.TransmitOptions`. Default to ``TransmitOptions.NONE.value``. - + Raises: InvalidOperatingModeException: if the XBee device's operating mode is not API or ESCAPED API. This method only checks the cached value of the operating mode. @@ -2308,10 +2308,10 @@ def _enter_at_command_mode(self): """ Attempts to put this device in AT Command mode. Only valid if device is working in AT mode. - + Returns: Boolean: ``True`` if the XBee device has entered in AT command mode, ``False`` otherwise. - + Raises: SerialTimeoutException: if there is any error trying to write within the serial port. """ @@ -2341,10 +2341,10 @@ def _enter_at_command_mode(self): def _determine_operating_mode(self): """ Determines and returns the operating mode of the XBee device. - + If the XBee device is not in AT command mode, this method attempts to enter on it. - + Returns: :class:`.OperatingMode` @@ -2368,26 +2368,24 @@ def _determine_operating_mode(self): def send_packet_sync_and_get_response(self, packet_to_send): """ - Perform all operations needed for a synchronous operation when the packet - listener is online. This operations are: - - 1. Puts "_sync_packet" to ``None``, to discard the last sync. packet read. + Send the packet synchronously and wait for the response. Steps: + + 1. Resets "_sync_packet" to ``None``, to discard the last sync. packet read. 2. Refresh "_sync_packet" to be used by the thread in charge of the synchronous read. - 3. Tells the packet listener that this XBee device is waiting for a packet with a determined frame ID. + 3. Notifies the packet listener to listen for a packet with the correct frame ID. 4. Sends the ``packet_to_send``. - 5. Waits the configured timeout for synchronous operations. - 6. Returns all attributes to a consistent state (except _sync_packet) - | 6.1. _sync_packet to ``None``. - | 6.2. notify the listener that we are no longer waiting for any packet. + 5. Waits for the preconfigured synchronous timeout. + 6. Returns all attributes to previous state (except _sync_packet) + | 6.1. Sets _sync_packet to ``None``. + | 6.2. Notifies the listener that we are no longer waiting for a packet. 7. Returns the received packet if it has arrived, ``None`` otherwise. This method must be only used when the packet listener is online. - + At the end of this method, the class attribute ``_sync_packet`` will be - the packet read by this method, or ``None`` if the previous was not possible. - Note that ``_sync_packet`` will remain being "the last packet read in a + the packet received or ``None`` if the response was not received. + Note that ``_sync_packet`` will persist as "the last packet read in a synchronous operation" until you call this method again. - Then, ``_sync_packet`` will be refreshed. Args: packet_to_send (:class:`.XBeePacket`): the packet to send. @@ -2396,7 +2394,7 @@ def send_packet_sync_and_get_response(self, packet_to_send): :class:`.XBeePacket`: the response packet obtained after sending the provided one. Raises: - TimeoutException: if the response is not received in the configured timeout. + TimeoutException: if the response is not received before the timeout. .. seealso:: | :class:`.XBeePacket` @@ -2435,22 +2433,22 @@ def send_packet(self, packet, sync=False): Sends a packet to the XBee device and waits for the response. The packet to send will be escaped or not depending on the current operating mode. - + This method can be synchronous or asynchronous. - + If is synchronous, this method will discard all response packets until it finds the one that has the appropriate frame ID, that is, the sent packet's frame ID. - + If is asynchronous, this method does not wait for any packet. Returns ``None``. - + Args: packet (:class:`.XBeePacket`): The packet to send. sync (Boolean): ``True`` to wait for the response of the sent packet and return it, ``False`` otherwise. - + Returns: :class:`.XBeePacket`: The response packet if ``sync`` is ``True``, ``None`` otherwise. - + Raises: TimeoutException: if ``sync`` is ``True`` and the response packet for the sent one cannot be read. InvalidOperatingModeException: if the XBee device's operating mode is not API or ESCAPED API. This @@ -2515,7 +2513,7 @@ def __build_expldata_packet(self, remote_xbee_device, data, src_endpoint, dest_e cluster_id, profile_id, broadcast=False, transmit_options=TransmitOptions.NONE.value): """ Builds and returns an explicit data packet with the provided parameters. - + Args: remote_xbee_device (:class:`.RemoteXBeeDevice`): the remote XBee device to send data to. data (String or Bytearray): the raw data to send. @@ -2527,10 +2525,10 @@ def __build_expldata_packet(self, remote_xbee_device, data, src_endpoint, dest_e ``False`` to send data to the specified ``remote_xbee_device``. transmit_options (Integer, optional): transmit options, bitfield of :class:`.TransmitOptions`. Default to ``TransmitOptions.NONE.value``. - + Returns: :class:`.ExplicitAddressingPacket`: the explicit packet generated with the provided parameters. - + Raises: All exceptions raised by :meth:`.ExplicitAddressingPacket.__init__` @@ -2590,7 +2588,7 @@ def __init__(self, port, baud_rate): Raises: All exceptions raised by :meth:`.XBeeDevice.__init__` constructor. - + .. seealso:: | :class:`.XBeeDevice` | :meth:`.XBeeDevice.__init__` @@ -2600,7 +2598,7 @@ def __init__(self, port, baud_rate): def open(self): """ Override. - + Raises: XBeeException: if the protocol is invalid. All exceptions raised by :meth:`.XBeeDevice.open`. @@ -2626,7 +2624,7 @@ def get_network(self): def get_protocol(self): """ Override. - + .. seealso:: | :meth:`.XBeeDevice.get_protocol` """ @@ -2731,7 +2729,7 @@ def get_network(self): def get_protocol(self): """ Override. - + .. seealso:: | :meth:`.XBeeDevice.get_protocol` """ @@ -2758,7 +2756,7 @@ def send_data_async_64(self, x64addr, data, transmit_options=TransmitOptions.NON def read_expl_data(self, timeout=None): """ Override. - + .. seealso:: | :meth:`.XBeeDevice.read_expl_data` """ @@ -2767,7 +2765,7 @@ def read_expl_data(self, timeout=None): def read_expl_data_from(self, remote_xbee_device, timeout=None): """ Override. - + .. seealso:: | :meth:`.XBeeDevice.read_expl_data_from` """ @@ -2777,7 +2775,7 @@ def send_expl_data(self, remote_xbee_device, data, src_endpoint, dest_endpoint, cluster_id, profile_id, transmit_options=TransmitOptions.NONE.value): """ Override. - + .. seealso:: | :meth:`.XBeeDevice.send_expl_data` """ @@ -2788,7 +2786,7 @@ def send_expl_data_broadcast(self, data, src_endpoint, dest_endpoint, cluster_id transmit_options=TransmitOptions.NONE.value): """ Override. - + .. seealso:: | :meth:`.XBeeDevice._send_expl_data_broadcast` """ @@ -2799,7 +2797,7 @@ def send_expl_data_async(self, remote_xbee_device, data, src_endpoint, dest_endp cluster_id, profile_id, transmit_options=TransmitOptions.NONE.value): """ Override. - + .. seealso:: | :meth:`.XBeeDevice.send_expl_data_async` """ @@ -2860,7 +2858,7 @@ def get_network(self): def get_protocol(self): """ Override. - + .. seealso:: | :meth:`.XBeeDevice.get_protocol` """ @@ -2887,7 +2885,7 @@ def send_data_async_64_16(self, x64addr, x16addr, data, transmit_options=Transmi def read_expl_data(self, timeout=None): """ Override. - + .. seealso:: | :meth:`.XBeeDevice.read_expl_data` """ @@ -2896,7 +2894,7 @@ def read_expl_data(self, timeout=None): def read_expl_data_from(self, remote_xbee_device, timeout=None): """ Override. - + .. seealso:: | :meth:`.XBeeDevice.read_expl_data_from` """ @@ -2906,7 +2904,7 @@ def send_expl_data(self, remote_xbee_device, data, src_endpoint, dest_endpoint, cluster_id, profile_id, transmit_options=TransmitOptions.NONE.value): """ Override. - + .. seealso:: | :meth:`.XBeeDevice.send_expl_data` """ @@ -2917,7 +2915,7 @@ def send_expl_data_broadcast(self, data, src_endpoint, dest_endpoint, cluster_id transmit_options=TransmitOptions.NONE.value): """ Override. - + .. seealso:: | :meth:`.XBeeDevice._send_expl_data_broadcast` """ @@ -2928,7 +2926,7 @@ def send_expl_data_async(self, remote_xbee_device, data, src_endpoint, dest_endp cluster_id, profile_id, transmit_options=TransmitOptions.NONE.value): """ Override. - + .. seealso:: | :meth:`.XBeeDevice.send_expl_data_async` """ @@ -2989,7 +2987,7 @@ def get_network(self): def get_protocol(self): """ Override. - + .. seealso:: | :meth:`.XBeeDevice.get_protocol` """ @@ -3034,7 +3032,7 @@ def send_data_async_64_16(self, x64addr, x16addr, data, transmit_options=Transmi def read_expl_data(self, timeout=None): """ Override. - + .. seealso:: | :meth:`.XBeeDevice._read_expl_data` """ @@ -3043,7 +3041,7 @@ def read_expl_data(self, timeout=None): def read_expl_data_from(self, remote_xbee_device, timeout=None): """ Override. - + .. seealso:: | :meth:`.XBeeDevice._read_expl_data_from` """ @@ -3053,7 +3051,7 @@ def send_expl_data(self, remote_xbee_device, data, src_endpoint, dest_endpoint, cluster_id, profile_id, transmit_options=TransmitOptions.NONE.value): """ Override. - + .. seealso:: | :meth:`.XBeeDevice._send_expl_data` """ @@ -3064,7 +3062,7 @@ def send_expl_data_broadcast(self, data, src_endpoint, dest_endpoint, cluster_id transmit_options=TransmitOptions.NONE.value): """ Override. - + .. seealso:: | :meth:`.XBeeDevice._send_expl_data_broadcast` """ @@ -3075,7 +3073,7 @@ def send_expl_data_async(self, remote_xbee_device, data, src_endpoint, dest_endp cluster_id, profile_id, transmit_options=TransmitOptions.NONE.value): """ Override. - + .. seealso:: | :meth:`.XBeeDevice.send_expl_data_async` """ @@ -3089,11 +3087,11 @@ def send_multicast_data(self, group_id, data, src_endpoint, dest_endpoint, """ Blocking method. This method sends multicast data to the provided group ID synchronously. - + This method will wait for the packet response. - + The default timeout for this method is :attr:`.XBeeDevice._DEFAULT_TIMEOUT_SYNC_OPERATIONS`. - + Args: group_id (:class:`.XBee16BitAddress`): the 16 bit address of the multicast group. data (Bytearray): the raw data to send. @@ -3101,10 +3099,10 @@ def send_multicast_data(self, group_id, data, src_endpoint, dest_endpoint, dest_endpoint (Integer): destination endpoint of the transmission. 1 byte. cluster_id (Integer): Cluster ID of the transmission. Must be between 0x0 and 0xFFFF. profile_id (Integer): Profile ID of the transmission. Must be between 0x0 and 0xFFFF. - + Returns: :class:`.XBeePacket`: the response packet. - + Raises: TimeoutException: if this method can't read a response packet in :attr:`.XBeeDevice._DEFAULT_TIMEOUT_SYNC_OPERATIONS` seconds. @@ -3122,16 +3120,16 @@ def send_multicast_data(self, group_id, data, src_endpoint, dest_endpoint, group_id, src_endpoint, dest_endpoint, cluster_id, profile_id, 0, TransmitOptions.ENABLE_MULTICAST.value, data) - + return self.send_packet_sync_and_get_response(packet_to_send) @AbstractXBeeDevice._before_send_method def send_multicast_data_async(self, group_id, data, src_endpoint, dest_endpoint, cluster_id, profile_id): """ Non-blocking method. This method sends multicast data to the provided group ID. - + This method won't wait for the response. - + Args: group_id (:class:`.XBee16BitAddress`): the 16 bit address of the multicast group. data (Bytearray): the raw data to send. @@ -3139,7 +3137,7 @@ def send_multicast_data_async(self, group_id, data, src_endpoint, dest_endpoint, dest_endpoint (Integer): destination endpoint of the transmission. 1 byte. cluster_id (Integer): Cluster ID of the transmission. Must be between 0x0 and 0xFFFF. profile_id (Integer): Profile ID of the transmission. Must be between 0x0 and 0xFFFF. - + Raises: TimeoutException: if this method can't read a response packet in :attr:`.XBeeDevice._DEFAULT_TIMEOUT_SYNC_OPERATIONS` seconds. @@ -3150,12 +3148,12 @@ def send_multicast_data_async(self, group_id, data, src_endpoint, dest_endpoint, .. seealso:: | :class:`XBee16BitAddress` """ - packet_to_send = ExplicitAddressingPacket(self._get_next_frame_id(), + packet_to_send = ExplicitAddressingPacket(self._get_next_frame_id(), XBee64BitAddress.UNKNOWN_ADDRESS, group_id, src_endpoint, dest_endpoint, cluster_id, profile_id, 0, TransmitOptions.ENABLE_MULTICAST.value, data) - + self.send_packet(packet_to_send) @@ -4736,7 +4734,7 @@ def __init__(self, local_xbee_device, x64bit_addr=XBee64BitAddress.UNKNOWN_ADDRE x16bit_addr=XBee16BitAddress.UNKNOWN_ADDRESS, node_id=None): """ Class constructor. Instantiates a new :class:`.RemoteXBeeDevice` with the provided parameters. - + Args: local_xbee_device (:class:`.XBeeDevice`): the local XBee device associated with the remote one. x64bit_addr (:class:`.XBee64BitAddress`): the 64-bit address of the remote XBee device. @@ -4759,7 +4757,7 @@ def __init__(self, local_xbee_device, x64bit_addr=XBee64BitAddress.UNKNOWN_ADDRE def get_parameter(self, parameter): """ Override. - + .. seealso:: | :meth:`.AbstractXBeeDevice.get_parameter` """ @@ -4776,7 +4774,7 @@ def get_parameter(self, parameter): RemoteATCmdOptions.NONE.value, parameter) response = self._local_xbee_device.send_packet_sync_and_get_response(packet_to_send) # raises TimeoutException - + if response.status != ATCommandStatus.OK: raise ATCommandException("Error getting parameter, command status: " + response.status.description) return response.command_value @@ -4784,7 +4782,7 @@ def get_parameter(self, parameter): def set_parameter(self, parameter, value): """ Override. - + .. seealso:: | :meth:`.AbstractXBeeDevice.set_parameter` """ @@ -4814,7 +4812,7 @@ def set_parameter(self, parameter, value): def is_remote(self): """ Override method. - + .. seealso:: | :meth:`.AbstractXBeeDevice.is_remote` """ @@ -4823,7 +4821,7 @@ def is_remote(self): def reset(self): """ Override method. - + .. seealso:: | :meth:`.AbstractXBeeDevice.reset` """ @@ -4836,10 +4834,10 @@ def reset(self): def get_local_xbee_device(self): """ Returns the local XBee device associated to the remote one. - + Returns: :class:`.XBeeDevice` - + """ return self._local_xbee_device @@ -4905,7 +4903,7 @@ def __init__(self, local_xbee_device, x64bit_addr=None, x16bit_addr=None, node_i def get_protocol(self): """ Override. - + .. seealso:: | :meth:`.RemoteXBeeDevice.get_protocol` """ @@ -4967,7 +4965,7 @@ def __init__(self, local_xbee_device, x64bit_addr=None, node_id=None): def get_protocol(self): """ Override. - + .. seealso:: | :meth:`.RemoteXBeeDevice.get_protocol` """ @@ -5005,7 +5003,7 @@ def __init__(self, local_xbee_device, x64bit_addr=None, node_id=None): def get_protocol(self): """ Override. - + .. seealso:: | :meth:`.RemoteXBeeDevice.get_protocol` """ @@ -5045,7 +5043,7 @@ def __init__(self, local_xbee_device, x64bit_addr=None, x16bit_addr=None, node_i def get_protocol(self): """ Override. - + .. seealso:: | :meth:`.RemoteXBeeDevice.get_protocol` """ @@ -5127,11 +5125,11 @@ def __init__(self, xbee_device): def start_discovery_process(self): """ Starts the discovery process. This method is not blocking. - + The discovery process will be running until the configured timeout expires or, in case of 802.15.4, until the 'end' packet is read. - + It may be that, after the timeout expires, there are devices that continue sending discovery packets to this XBee device. In this case, these devices will not be added to the network. @@ -5192,14 +5190,14 @@ def discover_devices(self, device_id_list): """ Blocking method. Attempts to discover a list of devices and add them to the current network. - + This method does not guarantee that all devices of ``device_id_list`` will be found, even if they exist physically. This will depend on the node discovery operation (``ND``) and timeout. - + Args: device_id_list (List): list of device IDs to discover. - + Returns: List: a list with the discovered devices. It may not contain all devices specified in ``device_id_list`` """ @@ -5211,7 +5209,7 @@ def discover_devices(self, device_id_list): def is_discovery_running(self): """ Returns whether the discovery process is running or not. - + Returns: Boolean: ``True`` if the discovery process is running, ``False`` otherwise. """ @@ -5220,11 +5218,11 @@ def is_discovery_running(self): def get_devices(self): """ Returns a copy of the XBee devices list of the network. - + If another XBee device is added to the list before the execution of this method, this XBee device will not be added to the list returned by this method. - + Returns: List: a copy of the XBee devices list of the network. """ @@ -5327,10 +5325,10 @@ def clear(self): def get_discovery_options(self): """ Returns the network discovery process options. - + Returns: Bytearray: the parameter value. - + Raises: TimeoutException: if the response is not received before the read timeout expires. XBeeException: if the XBee device's serial port is closed. @@ -5367,7 +5365,7 @@ def set_discovery_options(self, options): def get_discovery_timeout(self): """ Returns the network discovery timeout. - + Returns: Float: the network discovery timeout. @@ -5385,10 +5383,10 @@ def get_discovery_timeout(self): def set_discovery_timeout(self, discovery_timeout): """ Sets the discovery network timeout. - + Args: discovery_timeout (Float): timeout in seconds. - + Raises: TimeoutException: if the response is not received before the read timeout expires. XBeeException: if the XBee device's serial port is closed. @@ -5486,14 +5484,14 @@ def get_device_by_node_id(self, node_id): def add_if_not_exist(self, x64bit_addr=None, x16bit_addr=None, node_id=None): """ Adds an XBee device with the provided parameters if it does not exist in the current network. - + If the XBee device already exists, its data will be updated with the provided parameters that are not ``None``. - + Args: x64bit_addr (:class:`XBee64BitAddress`, optional): XBee device's 64bit address. Optional. x16bit_addr (:class:`XBee16BitAddress`, optional): XBee device's 16bit address. Optional. node_id (String, optional): the node identifier of the XBee device. Optional. - + Returns: :class:`.RemoteXBeeDevice`: the remote XBee device with the updated parameters. If the XBee device was not in the list yet, this method returns the given XBee device without changes. @@ -5504,13 +5502,13 @@ def add_if_not_exist(self, x64bit_addr=None, x16bit_addr=None, node_id=None): def add_remote(self, remote_xbee_device): """ Adds the provided remote XBee device to the network if it is not contained yet. - + If the XBee device is already contained in the network, its data will be updated with the parameters of the XBee device that are not ``None``. - + Args: remote_xbee_device (:class:`.RemoteXBeeDevice`): the remote XBee device to add to the network. - + Returns: :class:`.RemoteXBeeDevice`: the provided XBee device with the updated parameters. If the XBee device was not in the list yet, this method returns it without changes. @@ -5526,10 +5524,10 @@ def add_remote(self, remote_xbee_device): def add_remotes(self, remote_xbee_devices): """ Adds a list of remote XBee devices to the network. - + If any XBee device of the list is already contained in the network, its data will be updated with the parameters of the XBee device that are not ``None``. - + Args: remote_xbee_devices (List): the list of :class:`.RemoteXBeeDevice` to add to the network. """ @@ -5539,10 +5537,10 @@ def add_remotes(self, remote_xbee_devices): def remove_device(self, remote_xbee_device): """ Removes the provided remote XBee device from the network. - + Args: remote_xbee_device (:class:`.RemoteXBeeDevice`): the remote XBee device to be removed from the list. - + Raises: ValueError: if the provided :class:`.RemoteXBeeDevice` is not in the network. """ @@ -5551,7 +5549,7 @@ def remove_device(self, remote_xbee_device): def get_discovery_callbacks(self): """ Returns the API callbacks that are used in the device discovery process. - + This callbacks notify the user callbacks for each XBee device discovered. Returns: @@ -5613,9 +5611,9 @@ def discovery_spec_callback(xbee_packet): def _get_discovery_thread(self): """ Returns the network discovery thread. - + Used to determine whether the discovery thread is alive or not. - + Returns: :class:`.Thread`: the network discovery thread. """ @@ -5626,7 +5624,7 @@ def __check_nd_packet(xbee_packet): """ Checks if the provided XBee packet is an ND response or not. If so, checks if is the 'end' signal of the discovery process or if it has information about a remote XBee device. - + Returns: Integer: the ID that indicates if the packet is a finish discovery signal or if it contains information about a remote XBee device, or ``None`` if the ``xbee_packet`` is not a response for an ``ND`` command. @@ -5663,7 +5661,7 @@ def __discover_devices(self, node_id=None): try: init_time = time.time() - # In 802.15.4 devices, the discovery finishes when the 'end' command + # In 802.15.4 devices, the discovery finishes when the 'end' command # is received, so it's not necessary to calculate the timeout. # This also applies to S1B devices working in compatibility mode. is_802_compatible = self.__is_802_compatible() @@ -5700,13 +5698,13 @@ def __discover_devices(self, node_id=None): def __is_802_compatible(self): """ - Checks if the device performing the node discovery is a legacy + Checks if the device performing the node discovery is a legacy 802.15.4 device or a S1B device working in compatibility mode. - + Returns: Boolean: ``True`` if the device performing the node discovery is a legacy 802.15.4 device or S1B in compatibility mode, ``False`` otherwise. - + """ if self.__xbee_device.get_protocol() != XBeeProtocol.RAW_802_15_4: return False @@ -5722,13 +5720,13 @@ def __is_802_compatible(self): def __calculate_timeout(self): """ Determines the discovery timeout. - + Gets timeout information from the device and applies the proper corrections to it. - + If the timeout cannot be determined getting it from the device, this method returns the default timeout for discovery operations. - + Returns: Float: discovery timeout in seconds. """ @@ -5772,11 +5770,11 @@ def __create_remote(self, discovery_data): Creates and returns a :class:`.RemoteXBeeDevice` from the provided data, if the data contains the required information and in the required format. - + Returns: :class:`.RemoteXBeeDevice`: the remote XBee device generated from the provided data if the data provided is correct and the XBee device's protocol is valid, ``None`` otherwise. - + .. seealso:: | :meth:`.XBeeNetwork.__get_data_for_remote` """ @@ -5801,10 +5799,10 @@ def __get_data_for_remote(self, data): Extracts the :class:`.XBee16BitAddress` (bytes 0 and 1), the :class:`.XBee64BitAddress` (bytes 2 to 9) and the node identifier from the provided data. - + Args: data (Bytearray): the data to extract information from. - + Returns: Tuple (:class:`.XBee16BitAddress`, :class:`.XBee64BitAddress`, Bytearray): remote device information """ diff --git a/digi/xbee/io.py b/digi/xbee/io.py index 966d0c3..82d3e4d 100644 --- a/digi/xbee/io.py +++ b/digi/xbee/io.py @@ -20,10 +20,10 @@ @unique class IOLine(Enum): """ - Enumerates the different IO lines that can be found in the XBee devices. + Enumerates the different IO lines that can be found in the XBee devices. - Depending on the hardware and firmware of the device, the number of lines - that can be used as well as their functionality may vary. Refer to the + Depending on the hardware and firmware of the device, the number of lines + that can be used as well as their functionality may vary. Refer to the product manual to learn more about the IO lines of your XBee device. """ @@ -180,13 +180,13 @@ def get(cls, code): class IOSample(object): """ This class represents an IO Data Sample. The sample is built using the - the constructor. The sample contains an analog and digital mask indicating + the constructor. The sample contains an analog and digital mask indicating which IO lines are configured with that functionality. - Depending on the protocol the XBee device is executing, the digital and - analog masks are retrieved in separated bytes (2 bytes for the digital mask - and 1 for the analog mask) or merged contained (digital and analog masks - are contained in 2 bytes). + Depending on the protocol the XBee device is executing, the digital and + analog masks are retrieved in separated bytes (2 bytes for the digital mask + and 1 for the analog mask) or merged contained (digital and analog masks + are contained in 2 bytes). Digital and analog channels masks Indicates which digital and ADC IO lines are configured in the module. Each @@ -214,12 +214,12 @@ class IOSample(object): 0 0 0 0 1 1 0 0 0 0 1 0 1 0 0 1 Digital Channel Mask - Indicates which digital IO lines are configured in the module. Each bit + Indicates which digital IO lines are configured in the module. Each bit corresponds to one digital IO line on the module: :: bit 0 = DIO0AD0 - bit 1 = DIO1AD1 + bit 1 = DIO1AD1 bit 2 = DIO2AD2 bit 3 = DIO3AD3 bit 4 = DIO4AD4 @@ -239,7 +239,7 @@ class IOSample(object): 0 0 0 0 0 1 0 0 0 0 0 0 1 0 1 1 Analog Channel Mask - Indicates which lines are configured as ADC. Each bit in the analog + Indicates which lines are configured as ADC. Each bit in the analog channel mask corresponds to one ADC line on the module. :: @@ -252,7 +252,7 @@ class IOSample(object): bit 6 = NA bit 7 = Supply Voltage Value - Example: mask of 0x83 means AD0, and AD1 enabled. + Example: mask of 0x3 means AD0, and AD1 enabled. 0 0 0 0 0 0 1 1 """ @@ -327,8 +327,8 @@ def min_io_sample_payload(): def __parse_raw_io_sample(self): """ - Parses the information contained in the IO sample bytes reading the - value of each configured DIO and ADC. + Parses the information contained in the IO sample bytes reading the + value of each configured DIO and ADC. (802.15.4 only) """ data_index = 3 @@ -380,7 +380,7 @@ def __parse_raw_io_sample(self): def __parse_io_sample(self): """ - Parses the information contained in the IO sample bytes reading the + Parses the information contained in the IO sample bytes reading the value of each configured DIO and ADC. """ data_index = 4 @@ -465,7 +465,7 @@ def __get_digital_values(self): """ Returns the digital values map. - To verify if this sample contains a valid digital values, use the + To verify if this sample contains a valid digital values, use the method :meth:`.IOSample.has_digital_values`. Returns: @@ -486,7 +486,7 @@ def __get_analog_values(self): """ Returns the analog values map. - To verify if this sample contains a valid analog values, use the + To verify if this sample contains a valid analog values, use the method :meth:`.IOSample.has_analog_values`. Returns: @@ -498,7 +498,7 @@ def __get_power_supply_value(self): """ Returns the value of the power supply voltage. - To verify if this sample contains the power supply voltage, use the + To verify if this sample contains the power supply voltage, use the method :meth:`.IOSample.has_power_supply_value`. Returns: @@ -621,7 +621,7 @@ def get_analog_value(self, io_line): class IOMode(Enum): """ - Enumerates the different Input/Output modes that an IO line can be + Enumerates the different Input/Output modes that an IO line can be configured with. """ diff --git a/digi/xbee/packets/common.py b/digi/xbee/packets/common.py index c330664..78b19df 100644 --- a/digi/xbee/packets/common.py +++ b/digi/xbee/packets/common.py @@ -2023,6 +2023,8 @@ class ExplicitAddressingPacket(XBeeAPIPacket): The coordinator can be addressed by either setting the 64-bit address to all ``0x00`` and the 16-bit address to ``0xFFFE``, OR by setting the 64-bit address to the coordinator's 64-bit address and the 16-bit address to ``0x0000``. + Note: Not all networks have a coordinator. IP networks and Zigbee networks using + the distributed trust center topology will not have coordinators. For all other transmissions, setting the 16-bit address to the correct 16-bit address can help improve performance when transmitting to diff --git a/digi/xbee/serial.py b/digi/xbee/serial.py index f2a657c..8f4ace7 100644 --- a/digi/xbee/serial.py +++ b/digi/xbee/serial.py @@ -13,13 +13,20 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. from serial import Serial, EIGHTBITS, STOPBITS_ONE, PARITY_NONE -import enum +from enum import Enum import digi.xbee.exception -class FlowControl(enum.Enum): +class FlowControl(Enum): """ This class represents all available flow controls. + + | Values: + | **NONE** = None + | **SOFTWARE** = 0 + | **HARDWARE_RTS_CTS** = 1 + | **HARDWARE_DSR_DTR** = 2 + | **UNKNOWN** = 99 """ NONE = None diff --git a/doc/examples.rst b/doc/examples.rst index 884e231..9798f57 100644 --- a/doc/examples.rst +++ b/doc/examples.rst @@ -188,10 +188,10 @@ You can locate the example in the following path: Send explicit data `````````````````` -This sample application shows how to send data in application layer (explicit) -format to a remote ZigBee device on the same network as the local one using the -XBee Python Library. In this example, the XBee module sends explicit data using -a reliable transmission method. The application blocks during the transmission +This sample application shows how to send data in the application layer +(explicit) format to a remote ZigBee using the XBee Python Library. +In this example, the XBee module sends explicit data using a reliable +transmission method. The application blocks during the transmission request, but you are notified if there is any error during the process. You can locate the example in the following path: diff --git a/doc/getting_started_with_xbee_python_library.rst b/doc/getting_started_with_xbee_python_library.rst index 0b0e9a1..cf3cf6d 100644 --- a/doc/getting_started_with_xbee_python_library.rst +++ b/doc/getting_started_with_xbee_python_library.rst @@ -179,7 +179,7 @@ ZigBee devices * One of the devices must be a coordinator - Function: ZigBee Coordinator API - * Digi recommends the other one is a router - Function: ZigBee Router AP. + * Digi recommends the other one is a router - Function: ZigBee Router API. .. note:: If any of the two previous conditions is not satisfied, you must change @@ -194,7 +194,7 @@ ZigBee devices * If the device has the **CE** parameter, set it to **Enabled** in the coordinator. -#. Configure **ID** (PAN ID) setting to **C001BEE**. +#. Configure **ID** (PAN ID) setting to **CAFE**. #. Configure **SC** (Scan Channels) setting to **FFF**. #. Click **Write radio settings** in the **Radio Configuration** toolbar to apply the new values to the module. diff --git a/doc/user_doc/communicating_with_xbee_devices.rst b/doc/user_doc/communicating_with_xbee_devices.rst index 52566bb..452ce45 100644 --- a/doc/user_doc/communicating_with_xbee_devices.rst +++ b/doc/user_doc/communicating_with_xbee_devices.rst @@ -258,7 +258,7 @@ Send explicit data ------------------ Some ZigBee applications may require communication with third-party (non-Digi) -RF modules. These applications often send data of different public profiles +RF modules. These applications often send data on different public profiles such as Home Automation or Smart Energy to other modules. XBee ZigBee modules offer a special type of frame for this purpose. Explicit diff --git a/doc/user_doc/handling_analog_and_digital_io_lines.rst b/doc/user_doc/handling_analog_and_digital_io_lines.rst index e78524c..25cffec 100755 --- a/doc/user_doc/handling_analog_and_digital_io_lines.rst +++ b/doc/user_doc/handling_analog_and_digital_io_lines.rst @@ -1,9 +1,9 @@ Handle analog and digital IO lines ================================== -All the XBee modules, regardless of the protocol they run, have a set of lines -(pins). You can use these pins to connect sensors or actuators and configure -them with specific behavior. +All the XBee modules, regardless of the protocol they run, have a set of IO +lines (pins). You can use these pins to connect sensors or actuators and +configure them with specific behavior. You can configure the IO lines of an XBee device to be digital input/output (DIO), analog to digital converter (ADC), or pulse-width modulation output @@ -208,8 +208,8 @@ ADC ``` When you configure an IO line as analog to digital converter (ADC), you can -only read its value (counts) with ``get_adc_value()``. In this case, the method -used to read ADCs is different than the digital I/O method, but the parameter +only read its value (counts) with ``get_adc_value()``. In this case, the method +used to read ADCs is different than the digital I/O method, but the parameter provided is the same: the IO line to read the value from. **Read ADC values** diff --git a/doc/user_doc/xbee_terminology.rst b/doc/user_doc/xbee_terminology.rst index 7244b47..d488dd2 100644 --- a/doc/user_doc/xbee_terminology.rst +++ b/doc/user_doc/xbee_terminology.rst @@ -24,9 +24,9 @@ to deploy wireless technology. Multiple protocols and RF features are available, giving customers enormous flexibility to choose the best technology for their needs. -The XBee RF modules are available in two form factors: Through-Hole and Surface -Mount, with different antenna options. Almost all modules are available in the -Through-Hole form factor and share the same footprint. +The XBee RF modules are available in three form factors: Through-Hole, Surface +Mount, and Micro, with different antenna options. Almost all modules are available +in the Through-Hole form factor and share the same footprint. .. image:: ../images/concepts_form_factor.jpg :align: center @@ -38,8 +38,8 @@ Radio firmware -------------- Radio firmware is the program code stored in the radio module's persistent -memory that provides the control program for the device. From the local web -interface of the XBee Gateway, you can update or change the firmware of the +memory that provides the control program for the device. From XCTU or the local +web interface of the XBee Gateway, you can update or change the firmware of the local XBee module or any other module connected to the same network. This is a common task when changing the role of the device or updating to the latest version of the firmware.