Skip to content

Commit 42d012f

Browse files
committed
ble: add several methods related to the Bluetooth interface
- enable_bluetooth() - disable_bluetooth() - get_bluetooth_mac_addr() - update_bluetooth_password(String) Added also a new chapter in the documentation explaining these new methods. Signed-off-by: Ruben Moral <ruben.moral@digi.com>
1 parent 334a29b commit 42d012f

File tree

4 files changed

+270
-4
lines changed

4 files changed

+270
-4
lines changed

digi/xbee/devices.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import serial
2222
from serial.serialutil import SerialTimeoutException
2323

24+
import srp
25+
2426
from digi.xbee.packets.cellular import TXSMSPacket
2527
from digi.xbee.models.accesspoint import AccessPoint, WiFiEncryptionType
2628
from digi.xbee.models.atcomm import ATCommandResponse, ATCommand
@@ -61,6 +63,11 @@ class AbstractXBeeDevice(object):
6163
The default timeout for all synchronous operations, in seconds.
6264
"""
6365

66+
_BLE_API_USERNAME = "apiservice"
67+
"""
68+
The Bluetooth Low Energy API username.
69+
"""
70+
6471
LOG_PATTERN = "{port:<6s}{event:<12s}{opmode:<20s}{content:<50s}"
6572
"""
6673
Pattern used to log packet events.
@@ -1079,6 +1086,112 @@ def set_api_output_mode(self, api_output_mode):
10791086
"""
10801087
self.set_parameter("AO", bytearray([api_output_mode.code]))
10811088

1089+
def enable_bluetooth(self):
1090+
"""
1091+
Enables the Bluetooth interface of this XBee device.
1092+
1093+
To work with this interface, you must also configure the Bluetooth password if not done previously.
1094+
You can use the :meth:`.AbstractXBeeDevice.update_bluetooth_password` method for that purpose.
1095+
1096+
Note that your device must have Bluetooth Low Energy support to use this method.
1097+
1098+
Raises:
1099+
TimeoutException: if the response is not received before the read timeout expires.
1100+
XBeeException: if the XBee device's serial port is closed.
1101+
InvalidOperatingModeException: if the XBee device's operating mode is not API or ESCAPED API. This
1102+
method only checks the cached value of the operating mode.
1103+
"""
1104+
self._enable_bluetooth(True)
1105+
1106+
def disable_bluetooth(self):
1107+
"""
1108+
Disables the Bluetooth interface of this XBee device.
1109+
1110+
Note that your device must have Bluetooth Low Energy support to use this method.
1111+
1112+
Raises:
1113+
TimeoutException: if the response is not received before the read timeout expires.
1114+
XBeeException: if the XBee device's serial port is closed.
1115+
InvalidOperatingModeException: if the XBee device's operating mode is not API or ESCAPED API. This
1116+
method only checks the cached value of the operating mode.
1117+
"""
1118+
self._enable_bluetooth(False)
1119+
1120+
def _enable_bluetooth(self, enable):
1121+
"""
1122+
Enables or disables the Bluetooth interface of this XBee device.
1123+
1124+
Args:
1125+
enable (Boolean): ``True`` to enable the Bluetooth interface, ``False`` to disable it.
1126+
1127+
Raises:
1128+
TimeoutException: if the response is not received before the read timeout expires.
1129+
XBeeException: if the XBee device's serial port is closed.
1130+
InvalidOperatingModeException: if the XBee device's operating mode is not API or ESCAPED API. This
1131+
method only checks the cached value of the operating mode.
1132+
"""
1133+
self.set_parameter("BT", b'\x01' if enable else b'\x00')
1134+
self.write_changes()
1135+
self.apply_changes()
1136+
1137+
def get_bluetooth_mac_addr(self):
1138+
"""
1139+
Reads and returns the EUI-48 Bluetooth MAC address of this XBee device in a format such as ``00112233AABB``.
1140+
1141+
Note that your device must have Bluetooth Low Energy support to use this method.
1142+
1143+
Returns:
1144+
String: The Bluetooth MAC address.
1145+
1146+
Raises:
1147+
TimeoutException: if the response is not received before the read timeout expires.
1148+
XBeeException: if the XBee device's serial port is closed.
1149+
InvalidOperatingModeException: if the XBee device's operating mode is not API or ESCAPED API. This
1150+
method only checks the cached value of the operating mode.
1151+
"""
1152+
return utils.hex_to_string(self.get_parameter("BL"), False)
1153+
1154+
def update_bluetooth_password(self, new_password):
1155+
"""
1156+
Changes the password of this Bluetooth device with the new one provided.
1157+
1158+
Note that your device must have Bluetooth Low Energy support to use this method.
1159+
1160+
Args:
1161+
new_password (String): New Bluetooth password.
1162+
1163+
Raises:
1164+
TimeoutException: if the response is not received before the read timeout expires.
1165+
XBeeException: if the XBee device's serial port is closed.
1166+
InvalidOperatingModeException: if the XBee device's operating mode is not API or ESCAPED API. This
1167+
method only checks the cached value of the operating mode.
1168+
"""
1169+
# Generate the salt and verifier using the SRP library.
1170+
salt, verifier = srp.create_salted_verification_key(self._BLE_API_USERNAME, new_password,
1171+
hash_alg=srp.SHA256, ng_type=srp.NG_1024, salt_len=4)
1172+
1173+
# Ensure the verifier is 128 bytes.
1174+
verifier = (128 - len(verifier)) * b'\x00' + verifier
1175+
1176+
# Set the salt.
1177+
self.set_parameter("$S", salt)
1178+
1179+
# Set the verifier (split in 4 settings)
1180+
index = 0
1181+
at_length = int(len(verifier) / 4)
1182+
1183+
self.set_parameter("$V", verifier[index:(index + at_length)])
1184+
index += at_length
1185+
self.set_parameter("$W", verifier[index:(index + at_length)])
1186+
index += at_length
1187+
self.set_parameter("$X", verifier[index:(index + at_length)])
1188+
index += at_length
1189+
self.set_parameter("$Y", verifier[index:(index + at_length)])
1190+
1191+
# Write and apply changes.
1192+
self.write_changes()
1193+
self.apply_changes()
1194+
10821195
def _get_ai_status(self):
10831196
"""
10841197
Returns the current association status of this XBee device.

digi/xbee/util/utils.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,18 +238,21 @@ def int_to_length(number):
238238
return length
239239

240240

241-
def hex_to_string(byte_array):
241+
def hex_to_string(byte_array, pretty=True):
242242
"""
243243
Returns the provided bytearray in a pretty string format. All bytes are separated by blank spaces and
244244
printed in hex format.
245245
246246
Args:
247247
byte_array (Bytearray): the bytearray to print in pretty string.
248+
pretty (Boolean, optional): ``True`` for pretty string format, ``False`` for plain string format.
249+
Default to ``True``.
248250
249251
Returns:
250-
String: the bytearray formatted in a pretty string.
252+
String: the bytearray formatted in a string format.
251253
"""
252-
return " ".join(["%02X" % i for i in byte_array])
254+
separator = " " if pretty else ""
255+
return separator.join(["%02X" % i for i in byte_array])
253256

254257

255258
def doc_enum(enum_class, descriptions=None):

doc/user_doc/configuring_the_xbee_device.rst

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,3 +630,152 @@ with the following methods:
630630
+-------------+---------------------------+
631631
| DNS address | **set_dns_address()** |
632632
+-------------+---------------------------+
633+
634+
635+
.. _configBluetooth:
636+
637+
Configure Bluetooth settings
638+
----------------------------
639+
640+
Newer XBee3 devices have a Bluetooth® Low Energy (BLE) interface that enables
641+
you to connect your XBee device to another device such as a cellphone. The XBee
642+
device classes (local and remote) offer some methods that allow you to:
643+
644+
* :ref:`_configBluetoothEnableDisable`
645+
* :ref:`_configBluetoothConfigurePassword`
646+
* :ref:`_configBluetoothReadMacAddress`
647+
648+
649+
.. _configBluetoothEnableDisable:
650+
651+
Enable and disable Bluetooth
652+
````````````````````````````
653+
654+
Before connecting to your XBee device over Bluetooth Low Energy, you first have
655+
to enable this interface. The XBee Python Library provides a couple of methods
656+
to enable or disable this interface:
657+
658+
+-------------------------+------------------------------------------------------------------+
659+
| Method | Description |
660+
+=========================+==================================================================+
661+
| **enable_bluetooth()** | Enables the Bluetooth Low Energy interface of your XBee device. |
662+
+-------------------------+------------------------------------------------------------------+
663+
| **disable_bluetooth()** | Disables the Bluetooth Low Energy interface of your XBee device. |
664+
+-------------------------+------------------------------------------------------------------+
665+
666+
**Enabling and disabling the Bluetooth interface**
667+
668+
.. code:: python
669+
670+
[...]
671+
672+
# Instantiate an XBee device object.
673+
local_xbee = XBeeDevice("COM1", 9600)
674+
local_xbee.open()
675+
676+
# Enable the Bluetooth interface.
677+
local_xbee.enable_bluetooth()
678+
679+
[...]
680+
681+
# Disable the Bluetooth interface.
682+
local_xbee.disable_bluetooth()
683+
684+
[...]
685+
686+
These methods may fail for the following reasons:
687+
688+
* ACK of the command sent is not received in the configured timeout, throwing
689+
a ``TimeoutException``.
690+
* Other errors caught as ``XBeeException``:
691+
* The operating mode of the device is not ``API_MODE`` or
692+
``ESCAPED_API_MODE``, throwing an ``InvalidOperatingModeException``.
693+
* The response of the command is not valid, throwing an
694+
``ATCommandException``.
695+
* There is an error writing to the XBee interface, throwing a generic
696+
``XBeeException``.
697+
698+
699+
.. _configBluetoothConfigurePassword:
700+
701+
Configure the Bluetooth password
702+
````````````````````````````````
703+
704+
Once you have enabled the Bluetooth Low Energy, you must configure the password
705+
you will use to connect to the device over that interface (if not previously
706+
done). For this purpose, the API offers the following method:
707+
708+
+----------------------------------------+-----------------------------------------------------------+
709+
| Method | Description |
710+
+========================================+===========================================================+
711+
| **update_bluetooth_password(String)** | Specifies the new Bluetooth password of the XBee device. |
712+
+----------------------------------------+-----------------------------------------------------------+
713+
714+
**Configuring or changing the Bluetooth password**
715+
716+
.. code:: python
717+
718+
[...]
719+
720+
# Instantiate an XBee device object.
721+
local_xbee = XBeeDevice("COM1", 9600)
722+
local_xbee.open()
723+
724+
new_password = "myBluetoothPassword" # Do not hard-code it in the app!
725+
726+
# Configure the Bluetooth password.
727+
local_xbee.update_bluetooth_password(new_password)
728+
729+
[...]
730+
731+
The ``update_bluetooth_password`` method may fail for the following reasons:
732+
733+
* ACK of the command sent is not received in the configured timeout, throwing
734+
a ``TimeoutException``.
735+
* Other errors caught as ``XBeeException``:
736+
* The operating mode of the device is not ``API_MODE`` or
737+
``ESCAPED_API_MODE``, throwing an ``InvalidOperatingModeException``.
738+
* The response of the command is not valid, throwing an
739+
``ATCommandException``.
740+
* There is an error writing to the XBee interface, throwing a generic
741+
``XBeeException``.
742+
743+
.. warning::
744+
Never hard-code the Bluetooth password in the code, a malicious person could
745+
decompile the application and find it out.
746+
747+
748+
.. _configBluetoothReadMacAddress:
749+
750+
Read the Bluetooth MAC address
751+
``````````````````````````````
752+
753+
Another method that the XBee Java Library provides is
754+
``get_bluetooth_mac_addr()``, which returns the EUI-48 Bluetooth MAC address of
755+
your XBee device in a format such as "00112233AABB".
756+
757+
**Reading the Bluetooth MAC address**
758+
759+
.. code:: python
760+
761+
[...]
762+
763+
# Instantiate an XBee device object.
764+
local_xbee = XBeeDevice("COM1", 9600)
765+
local_xbee.open()
766+
767+
print("The Bluetooth MAC address is: %s" % local_xbee.get_bluetooth_mac_addr())
768+
769+
[...]
770+
771+
The ``get_bluetooth_mac_addr`` method may fail for the following reasons:
772+
773+
* ACK of the command sent is not received in the configured timeout, throwing
774+
a ``TimeoutException``.
775+
* Other errors caught as ``XBeeException``:
776+
* The operating mode of the device is not ``API_MODE`` or
777+
``ESCAPED_API_MODE``, throwing an ``InvalidOperatingModeException``.
778+
* The response of the command is not valid, throwing an
779+
``ATCommandException``.
780+
* There is an error writing to the XBee interface, throwing a generic
781+
``XBeeException``.

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
pyserial>=3
2-
sphinxcontrib.napoleon
2+
sphinxcontrib.napoleon
3+
srp

0 commit comments

Comments
 (0)