Adafruit BLE python library can't list descriptors - python

I'm trying to use BLE library for python to communicate with one Nordic nrf51844 chipset. Because one characteristic is notification enabled, I need to enable notification from client side by setting the descriptor Client Characteristic Configuration to 0x0001. But I failed to get the descriptor with the call "characteristic.find_descriptor()" to get it. I also tried to print out all of descriptors discovered, but looks like there is no luck to get it work.
Below is the code I'm using to discover characteristics and its descriptor referred to the example of Adafruit BLE library:
def enable_notification(characteristic):
_enableDesc = characteristic.find_descriptor(CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID)
_cmd = bytearray([0x01, 0x00])
_enableDesc.write_value(_cmd)
def handle_device_message(device):
global _status
# Once connected do everything else in a try/finally to make sure the device
# is disconnected when done.
# Wait for service discovery to complete for at least the specified
# service and characteristic UUID lists. Will time out after 60 seconds
# (specify timeout_sec parameter to override).
_logger.info('Discovering services...')
device.discover([HES_SERVICE_UUID], [DATA_CHAR_UUID, STATUS_CHAR_UUID, FACTOR_CHAR_UUID])
# Find the HES service and its characteristics.
hes = device.find_service(HES_SERVICE_UUID)
dataC = hes.find_characteristic(DATA_CHAR_UUID)
statusC = hes.find_characteristic(STATUS_CHAR_UUID)
#factorC = hes.find_characteristic(FACTOR_CHAR_UUID)
dataC.list_descriptors()
statusC.list_descriptors()
enable_notification(dataC)
enable_notification(statusC)
But it always failed at "characteristic.find_descriptor()" with below error:
_enableDesc =
characteristic.find_descriptor(CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID)
File "build/bdist.macosx-10.11-x86_64/egg/Adafruit_BluefruitLE/interfaces/gatt.py", line 98, in find_descriptor
File "build/bdist.macosx-10.11-x86_64/egg/Adafruit_BluefruitLE/corebluetooth/gatt.py", line 124, in list_descriptors
File "build/bdist.macosx-10.11-x86_64/egg/Adafruit_BluefruitLE/corebluetooth/metadata.py", line 63, in get_all
TypeError: 'NoneType' object is not iterable
I looked into the source code of library, but can't find the interface to get descriptors explicitly. Can anyone help me on this?
Thanks!

Finally I figured out by checking the API of IOS to set notification. It should be set by calling setNotify for characteristic instead of writeValue for descriptor. And for the descriptor stuff, it shows we need to wait for some time before all descriptors are discovered and returned. Might be the issue implemented with Python. Not really verified with IOS native program.
BTW, after setting the notification, we need to wait for some time as well before the device sends notification to client.
Will get a Linux box to verify the implementation with blueZ is working well.

Related

Python canopen send a domain

I'm using the canopen python library, see https://canopen.readthedocs.io/en/latest/index.html.
I'm trying to send a domain to my CANopen node:
# nodeHeadPort.sdo['Config Data2'].phys = b'\x11\x22\x33\x44\x55'
nodeHeadPort.sdo.download(0x6006, 0, b'\x11\x22\x33\x44\x55')
But the python gives an exception with:
canopen.sdo.exceptions.SdoAbortedError: Code 0x06090011, Subindex does not exist
And in the eds file I have the following:
[6006]
ParameterName=Config Data2
ObjectType=0x7
;StorageLocation=RAM
DataType=0x000F
AccessType=rw
DefaultValue=
PDOMapping=0
I guess my call in the python program should be different (without any subindex)? Does somebody know how to do?
This is what's going on the bus:
The reason for the issue was in the CANopen slave. I have to add their support for data type domain.
https://github.com/CANopenNode/CANopenDemo/blob/master/demo/domainDemo.c

Is there a way to pass objects connected to hardware to Robot Framework?

I would like to test a hardware device with the Robot Framework. Since I do not want to disconnect and connect my device every test case again, I would like to know if there is a possibility to have a Python object initialized outside of the Robot Framework but used inside it.
See my code exapmle as follows:
Main.py:
from robot import run
from SomeLibraryUsedByRobot import SomeLibraryUsedByRobot
device = Device()
SomeLibraryUsedByRobot.device = device
run('SomeRobotFile.robot')
SomeLibraryUsedByRobot.py:
class SomeLibraryUsedByRobot:
device = None
def access_device(self):
device.some_function()
SomeRobotFile.robot:
*** Settings ***
Library SomeLibraryUsedByRobot.py
*** Test Cases ***
Some Test
Access Device
The execution of the main file results in the error 'NoneType' object has no attribute some_function what brings me to the conclusion that the initialization of the field device of the library inside the main file is not working.
I also tried out my plans using the listener interface of the robot framework and its function library_import(self, name, attributes).
MyListener.py:
class MyListener:
ROBOT_LISTENER_API_VERSION = 2
def library_import(self, name, attributes):
if name == "SomeLibraryUsedByRobot":
device = Device()
SomeLibraryUsedByRobot.device = device
print( "SomeLibraryUsedByRobot initialized with device" )
When I tried it with the listener, I executed the robot file directly from console (and not with the main.py). I also added a line inside the library class: ROBOT_LIBRARY_SCOPE = 'TEST SUITE'.
The library_import(...) function definitely gets called, since I am seeing the print on the console. But the result is the same with the first try: 'NoneType' object has no attribute some_function.
Basically, we do not have to involve any hardware in this discussion. I just would like to pass some python objects from outside to the libraries used by the robot framework. Do you see any solution for that?
Create a keyword to open/init the device and a keyword to close/deinit the device inside your SomeLibraryUsedByRobot.
class SomeLibraryUsedByRobot:
# Called upon library import for more: http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#library-scope
def __init__(self):
self.device = None
def access_device(self):
self.device.some_function()
def init_device(self):
self.device = Device()
def deinit_device(self):
self.device.deinit()
self.device = None
Now to avoid connecting, disconnecting in every test case call these keywords in the Suite Setup/Suite Teardown.
*** Settings ***
Library SomeLibraryUsedByRobot
Suite Setup Init Device
Suite Teardown Deinit Device
*** Test Cases ***
Case 1
Access Device
Case 2
Access Device
Case 3
Access Device
Case 4
Access Device
Generally apply the approach used in other existing libraries like Telnet or SeleniumLibrary where there are Open ... and Close ... keywords to manage connections to a browser or in case of Telnet to a server or a hardware, etc.
With multiple devices you can use the robot.utils.connectioncache to manage multiple connections. This is used by many existing libraries out there.

Unable to set TIMEOUT for ldap in Python 2.7

I'd like to setup a "timeout" for the ldap library (python-ldap-2.4.15-2.el7.x86_64) and python 2.7
I'm forcing my /etc/hosts to resolve an non existing IP address in
order to raise the timeout.
I've followed several examples and took a look at the documentation or questions like this one without luck.
By now I've tried forcing global timeouts before the initialize:
ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 1)
ldap.set_option(ldap.OPT_TIMEOUT, 1)
ldap.protocol_version=ldap.VERSION3
Forced the same values at object level:
ldap_obj = ldap.initialize("ldap://%s:389" % LDAPSERVER ,trace_level=9)
ldap_obj.set_option(ldap.OPT_NETWORK_TIMEOUT, 1)
ldap_obj.set_option(ldap.OPT_TIMEOUT, 1)
I've also tried with:
ldap.network_timeout = 1
ldap.timelimit = 1
And used both methods, search and search_st (The synchronous form with timeout)
Finally this is the code:
def testLDAPConex( LDAPSERVER ) :
"""
Check ldap
"""
ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 1)
ldap.set_option(ldap.OPT_TIMEOUT, 1)
ldap.protocol_version=ldap.VERSION3
ldap.network_timeout = 1
ldap.timelimit = 1
try:
ldap_obj = ldap.initialize("ldap://%s:389" % LDAPSERVER ,trace_level=9)
ldap_result_id = ldap_obj.search("ou=people,c=this,o=place", ldap.SCOPE_SUBTREE, "cn=user")
I've printed the constants of the object OPT_NETWORK_TIMEOUT and OPT_TIMEOUT, the values were assigned correctly.
The execution time is 56s everytime and I'm not able to control the amount of seconds for the timeout.
Btw, the same code in python3 does work as intended:
real 0m10,094s
user 0m0,072s
sys 0m0,013s
After some testing I've decided to rollback the VM to a previous state.
The Networking Team were doing changes across the network configuration, that might have changed the behaviour over the interface and some kind of bug when the packages were being sent over the wire in:
ldap_result_id = ldap_obj.search("ou=people,c=this,o=place", ldap.SCOPE_SUBTREE, "cn=user")
After restoring the VM, which included a restart, the error of not being able to reach LDAP raises as expected.

Python: Get device "model" using pyvisa or pyserial

I wrote a data acquisition program/script that works with a device developed by our collaboration. The problem is that I can only read from this device. No writing is possible, so it's not possible to use a serial "?IDN*" command to know what device this is.
The only thing that defines this device is its "Model" that can be seen in "Devices and Printers" in Control Panel of Windows. The following figure shows it:
The guy who designed the device was able to create a labview simple program that extracts this name from the device through NI-VISA through something called "Intf Inst Name", which is called "Interface Information:Interface Description".
If I get this model name and compare it with the pyvisa device name, I'll be able to automatically detect the presence of our device, which is an important thing to have, in case a USB disconnect happens. This is because VISA opens the device through a name that can be different on every computer, but this name "GPS DATA LOGGER" is the same everywhere and always.
I need this solution to be cross-platform. That's why I need to use pyvisa or pyserial. Though any cross-platform alternative is OK.
So my question in brief: How can I use pyvisa/pyserial to find the model name corresponding to the device model (in my case "GPS DATA LOGGER")?
Please ask for any additional information you may require.
Update
I learned that there's an "attribute" pyvisa with the name "VI_ATTR_INTF_INST_NAME" that would get this name, but I don't know how to use it. Does anyone know how to read these attributes?
I found the way to do it. Unfortunately it involves opening every VISA device you have in your computer. I wrote a small pyvisa function that will do the task for you with comments. The function returns all the devices that contain the model name/descriptor mentioned as a parameter:
import pyvisa
def findInstrumentByDescriptor(descriptor):
devName = descriptor
rm = pyvisa.ResourceManager()
com_names=rm.list_resources()
devicesFound = []
#loop over all devices, open them, and check the descriptor
for com in range(len(com_names)):
try:
#try to open instrument, if failed, just skip to the next device
my_instrument=rm.open_resource(com_names[com])
except:
print("Failed to open " + com_names[com])
continue
try:
# VI_ATTR_INTF_INST_NAME is 3221160169, it contains the model name "GPS DATA LOGGER" (check pyvisa manual for other VISA attributes)
modelStr = my_instrument.get_visa_attribute(3221160169)
#search for the string you need inside the VISA attribute
if modelStr.find(devName) >= 0:
#if found, will be added to the array devicesFound
devicesFound.append(com_names[com])
my_instrument.close()
except:
#if exception is thrown here, then the device should be closed
my_instrument.close()
#return the list of devices that contain the VISA attribute required
return devicesFound
#here's the main call
print(findInstrumentByDescriptor("GPS DATA LOGGER"))
pyvisa has an optional query parameter for list_resources(), which you can use to narrow the scope of your search to just your device. The syntax for this is like a regular expression.
Try this:
from string import Template
VI_ATTR_INTF_INST_NAME = 3221160169
device_name = "GPS DATA LOGGER"
entries = dict(
ATTR = VI_ATTR_INTF_INST_NAME,
NAME = device_name )
query_template = Template(u'ASRL?*INSTR{$ATTR == "$NAME"}')
query = query_template.substitute(entries)
rm = visa.ResourceManager()
rm.list_resources(query)

pychef api pychef ChefServerNotFoundError

I'm using chef server, configuring few different nodes / environments.
when asking for env attributes using the pychef api, few times in a row (when refreshing a web page using python server calling chef server) im getting ChefServerNotFoundError (the first few times are fine, and third exception is raised)
I guess that there is kind of firewall / anti ddos attacks on this server, but i can not figure out how to edit these settings.
anyone have any idea?
this is a part of the method (that is called 3 times and throws an exception):
env_nodes = Search('node').query('chef_environment: {0}'.format(env_name))
nodes_dict = {}
for n in env_nodes:
node = Node(n['name'])
nodes_dict[node.name] = node['ipaddress']`
and this is the traceback:
File "C:\env\lib\site-packages\chef\search.py", line 91, in __getitem__
row_value = self.data['rows'][value]
File "C:\env\lib\site-packages\chef\search.py", line 59, in data
self._data = self.api[self.url]
TypeError: 'NoneType' object is not subscriptable`
When using PyChef in a webapp or other multi-threaded system you should really pass in the API object explicitly. There is a system to track a default API target in a threadlocal for the purposes of making simple scripts easier, but in retrospect this was probably a mistake as it leads to these confusing issues. This would be a better version of that code, also faster:
nodes_dict = {row.object.name: row.object['ipaddress'] for row in Search('node', 'chef_environment:{}'.format(env_name), api=api)}
Where api is the return value of chef.autoconfigure() or some other ChefAPI object.

Categories

Resources