androidpython-3.xpepper

How can I connect to Pepper (NAOqi 2.9) via libqi-python


Description:
Hi, I'm trying to connect to a Pepper running on NAOqi 2.9.5 through libqi-python. I've read that Pepper 2.9 generally isn't supported but I've seen some indications that it might be possible (1, 2). I tried to use the authentication_with_application.py example as the starting point (see the minimised version further down below) but I can't seem to get the connection to work. With port 9503 I at least don't get a connection error outright but the program still crashes and I get:

[W] 1707753982.910001 6507 qi.FutureSync: Error in future on destruction: 'disconnected' - continuing stack unwinding...

Would this even be possible with libqi-python? (1 makes it look like it could be, but since it's almost 5 years old, the API may have changed). If it's possible, what am I doing wrong? I'm using the same credentials as when connecting via SSH (which works fine). Any advice/hints would be greatly appreciated.

Setup:

MRE:

import qi
import sys 

class Authenticator:

    def __init__(self, username, password):
        self.username = username
        self.password = password

    # This method is expected by libqi and must return a dictionary containing
    # login information with the keys 'user' and 'token'.
    def initialAuthData(self):
        return {'user': self.username, 'token': self.password}


class AuthenticatorFactory:

    def __init__(self, username, password):
        self.username = username
        self.password = password

    # This method is expected by libqi and must return an object with at least
    # the `initialAuthData` method.
    def newAuthenticator(self):
        return Authenticator(self.username, self.password)

# Connect to the robot fails at app.start() => RuntimeError: disconnected
app = qi.Application(sys.argv, url="tcp://192.168.1.59:9503")
logins = ("nao", "OMITTED")
factory = AuthenticatorFactory(*logins)
app.session.setClientAuthenticatorFactory(factory)
app.start()
print("started")

# This doesn't work either => RuntimeError: disconnected
# session = qi.Session()
# logins = ("nao", "OMITTED")
# factory = AuthenticatorFactory(*logins)
# session.setClientAuthenticatorFactory(factory)
# session.connect("tcp://192.168.1.59:9503")

tts = app.session.service("ALTextToSpeech")
tts.say("Hello there!")

Logs:

Terminal output:

ost@ost:~$ python3 test.py --qi-log-level verbose
[W] 1707753982.876091 6507 qi.path.sdklayout: No Application was created, trying to deduce paths
[V] 1707753982.876339 6507 qi.applicationsession: Connect URL is now: tcp://192.168.1.59:9503
[V] 1707753982.876347 6507 qi.applicationsession: Listen URLs are now: tcp://127.0.0.1:0
[V] 1707753982.876671 6507 qi.eventloop: start: thread count limits: initial (minimum, maximum) before any adjustment = (-1, 0)
[V] 1707753982.876729 6507 qi.eventloop: start: thread count limits: min <- 4 (read from environment variable QI_EVENTLOOP_MIN_THREADS, with default 4)
[V] 1707753982.876755 6507 qi.eventloop: start: thread count limits: max <- 150 (read from environment variable QI_EVENTLOOP_MAX_THREADS, with default 150)
[V] 1707753982.876758 6507 qi.eventloop: start: thread count limits: final (minimum, maximum) after potential adjustment = (4, 150)
[V] 1707753982.876760 6507 qi.eventloop: start: number of threads that will be launched = 4 (between (min, max) = (4, 150))
[V] 1707753982.877437 6507 qitype.type: Shared pointer to unknown type N2qi13MessageSocketE, assuming object not yet registered
[V] 1707753982.877517 6507 qitype.type: Shared pointer to unknown type N2qi13MessageSocketE, assuming object not yet registered
[V] 1707753982.877532 6507 qitype.type: Shared pointer to unknown type N2qi13MessageSocketE, assuming object not yet registered
[V] 1707753982.877536 6507 qitype.type: Shared pointer to unknown type N2qi13MessageSocketE, assuming object not yet registered
[V] 1707753982.877539 6507 qitype.type: Shared pointer to unknown type N2qi13MessageSocketE, assuming object not yet registered
[V] 1707753982.877760 6507 qitype.type: Shared pointer to unknown type N2qi13MessageSocketE, assuming object not yet registered
[V] 1707753982.877830 6507 qitype.type: registerType: access to type factory before registration detected for type N2qi16ServiceDirectoryE
[V] 1707753982.877872 6507 qitype.type: registerType: access to type factory before registration detected for type N2qi6FutureImEE
[V] 1707753982.878001 6507 qitype.type: registerType: access to type factory before registration detected for type N2qi6FutureINS_8AnyValueEEE
[V] 1707753982.878212 6507 qitype.type: registerType: access to type factory before registration detected for type N2qi11BoundObjectE
[V] 1707753982.878368 6507 qitype.type: registerType: access to type factory before registration detected for type N2qi10FutureSyncINS_8AnyValueEEE
[V] 1707753982.878471 6507 qitype.type: registerType: access to type factory before registration detected for type N2qi7PromiseINS_8AnyValueEEE
[V] 1707753982.878734 6507 qi.python.object: Registration of method __class__ is ignored as it is private.
[V] 1707753982.878771 6507 qi.python.object: Registration of method __delattr__ is ignored as it is private.
[V] 1707753982.878803 6507 qi.python.object: Registration of method __dir__ is ignored as it is private.
[V] 1707753982.878806 6507 qi.python.object: The object attribute '__doc__' has value 'None', and will therefore be ignored.
[V] 1707753982.878810 6507 qi.python.object: Registration of method __eq__ is ignored as it is private.
[V] 1707753982.878814 6507 qi.python.object: Registration of method __format__ is ignored as it is private.
[V] 1707753982.878817 6507 qi.python.object: Registration of method __ge__ is ignored as it is private.
[V] 1707753982.878821 6507 qi.python.object: Registration of method __getattribute__ is ignored as it is private.
[V] 1707753982.878824 6507 qi.python.object: Registration of method __gt__ is ignored as it is private.
[V] 1707753982.878828 6507 qi.python.object: Registration of method __hash__ is ignored as it is private.
[V] 1707753982.878834 6507 qi.python.object: Registration of method __init__ is ignored as it is private.
[V] 1707753982.878838 6507 qi.python.object: Registration of method __init_subclass__ is ignored as it is private.
[V] 1707753982.878841 6507 qi.python.object: Registration of method __le__ is ignored as it is private.
[V] 1707753982.878845 6507 qi.python.object: Registration of method __lt__ is ignored as it is private.
[V] 1707753982.878850 6507 qi.python.object: Registration of method __ne__ is ignored as it is private.
[V] 1707753982.878853 6507 qi.python.object: Registration of method __new__ is ignored as it is private.
[V] 1707753982.878856 6507 qi.python.object: Registration of method __reduce__ is ignored as it is private.
[V] 1707753982.878860 6507 qi.python.object: Registration of method __reduce_ex__ is ignored as it is private.
[V] 1707753982.878862 6507 qi.python.object: Registration of method __repr__ is ignored as it is private.
[V] 1707753982.878866 6507 qi.python.object: Registration of method __setattr__ is ignored as it is private.
[V] 1707753982.878869 6507 qi.python.object: Registration of method __sizeof__ is ignored as it is private.
[V] 1707753982.878872 6507 qi.python.object: Registration of method __str__ is ignored as it is private.
[V] 1707753982.878875 6507 qi.python.object: Registration of method __subclasshook__ is ignored as it is private.
[V] 1707753982.878878 6507 qi.python.object: The object attribute '__weakref__' has value 'None', and will therefore be ignored.
[V] 1707753982.879015 6507 qi.python.object: Registration of method newAuthenticator with signature () -> m.
[V] 1707753982.879175 6507 qi.python.object: Registration of method __class__ is ignored as it is private.
[V] 1707753982.879210 6507 qi.python.object: Registration of method __delattr__ is ignored as it is private.
[V] 1707753982.879218 6507 qi.python.object: Registration of method __dir__ is ignored as it is private.
[V] 1707753982.879220 6507 qi.python.object: The object attribute '__doc__' has value 'None', and will therefore be ignored.
[V] 1707753982.879224 6507 qi.python.object: Registration of method __eq__ is ignored as it is private.
[V] 1707753982.879227 6507 qi.python.object: Registration of method __format__ is ignored as it is private.
[V] 1707753982.879230 6507 qi.python.object: Registration of method __ge__ is ignored as it is private.
[V] 1707753982.879238 6507 qi.python.object: Registration of method __getattribute__ is ignored as it is private.
[V] 1707753982.879241 6507 qi.python.object: Registration of method __gt__ is ignored as it is private.
[V] 1707753982.879244 6507 qi.python.object: Registration of method __hash__ is ignored as it is private.
[V] 1707753982.879247 6507 qi.python.object: Registration of method __init__ is ignored as it is private.
[V] 1707753982.879251 6507 qi.python.object: Registration of method __init_subclass__ is ignored as it is private.
[V] 1707753982.879254 6507 qi.python.object: Registration of method __le__ is ignored as it is private.
[V] 1707753982.879258 6507 qi.python.object: Registration of method __lt__ is ignored as it is private.
[V] 1707753982.879261 6507 qi.python.object: Registration of method __ne__ is ignored as it is private.
[V] 1707753982.879265 6507 qi.python.object: Registration of method __new__ is ignored as it is private.
[V] 1707753982.879271 6507 qi.python.object: Registration of method __reduce__ is ignored as it is private.
[V] 1707753982.879280 6507 qi.python.object: Registration of method __reduce_ex__ is ignored as it is private.
[V] 1707753982.879284 6507 qi.python.object: Registration of method __repr__ is ignored as it is private.
[V] 1707753982.879287 6507 qi.python.object: Registration of method __setattr__ is ignored as it is private.
[V] 1707753982.879290 6507 qi.python.object: Registration of method __sizeof__ is ignored as it is private.
[V] 1707753982.879315 6507 qi.python.object: Registration of method __str__ is ignored as it is private.
[V] 1707753982.879321 6507 qi.python.object: Registration of method __subclasshook__ is ignored as it is private.
[V] 1707753982.879326 6507 qi.python.object: Registration of method __weakref__ is ignored as it is private.
[V] 1707753982.879380 6507 qi.python.object: Registration of method newAuthenticator with signature () -> m.
[V] 1707753982.879816 6507 qi.eventloop: start: thread count limits: initial (minimum, maximum) before any adjustment = (1, 1)
[V] 1707753982.879963 6507 qi.eventloop: start: thread count limits: final (minimum, maximum) after potential adjustment = (1, 1)
[V] 1707753982.879977 6507 qi.eventloop: start: number of threads that will be launched = 1 (between (min, max) = (1, 1))
[V] 1707753982.880133 6507 qimessaging.messagesocket: (ResolverUrlList)0x5586f8db1ec0: Trying to connect to 192.168.1.59:9503
[V] 1707753982.888069 6508 qitype.dynamicobject: Return signature might be incorrect depending on the value, from m to o
[V] 1707753982.888491 6511 qi.python.object: Registration of method __class__ is ignored as it is private.
[V] 1707753982.888648 6511 qi.python.object: Registration of method __delattr__ is ignored as it is private.
[V] 1707753982.888663 6511 qi.python.object: Registration of method __dir__ is ignored as it is private.
[V] 1707753982.888667 6511 qi.python.object: The object attribute '__doc__' has value 'None', and will therefore be ignored.
[V] 1707753982.888672 6511 qi.python.object: Registration of method __eq__ is ignored as it is private.
[V] 1707753982.888677 6511 qi.python.object: Registration of method __format__ is ignored as it is private.
[V] 1707753982.888682 6511 qi.python.object: Registration of method __ge__ is ignored as it is private.
[V] 1707753982.888687 6511 qi.python.object: Registration of method __getattribute__ is ignored as it is private.
[V] 1707753982.888691 6511 qi.python.object: Registration of method __gt__ is ignored as it is private.
[V] 1707753982.888694 6511 qi.python.object: Registration of method __hash__ is ignored as it is private.
[V] 1707753982.888699 6511 qi.python.object: Registration of method __init__ is ignored as it is private.
[V] 1707753982.888705 6511 qi.python.object: Registration of method __init_subclass__ is ignored as it is private.
[V] 1707753982.888716 6511 qi.python.object: Registration of method __le__ is ignored as it is private.
[V] 1707753982.888723 6511 qi.python.object: Registration of method __lt__ is ignored as it is private.
[V] 1707753982.888732 6511 qi.python.object: Registration of method __ne__ is ignored as it is private.
[V] 1707753982.888767 6511 qi.python.object: Registration of method __new__ is ignored as it is private.
[V] 1707753982.888797 6511 qi.python.object: Registration of method __reduce__ is ignored as it is private.
[V] 1707753982.888804 6511 qi.python.object: Registration of method __reduce_ex__ is ignored as it is private.
[V] 1707753982.888809 6511 qi.python.object: Registration of method __repr__ is ignored as it is private.
[V] 1707753982.888812 6511 qi.python.object: Registration of method __setattr__ is ignored as it is private.
[V] 1707753982.888816 6511 qi.python.object: Registration of method __sizeof__ is ignored as it is private.
[V] 1707753982.888820 6511 qi.python.object: Registration of method __str__ is ignored as it is private.
[V] 1707753982.888824 6511 qi.python.object: Registration of method __subclasshook__ is ignored as it is private.
[V] 1707753982.888828 6511 qi.python.object: The object attribute '__weakref__' has value 'None', and will therefore be ignored.
[V] 1707753982.888946 6511 qi.python.object: Registration of method initialAuthData with signature () -> m.
[V] 1707753982.889346 6508 qitype.dynamicobject: Return signature might be incorrect depending on the value, from m to {sm}
[V] 1707753982.909155 6509 qimessaging.server: Closing server...
[W] 1707753982.910001 6507 qi.FutureSync: Error in future on destruction: 'disconnected' - continuing stack unwinding...
Traceback (most recent call last):
  File "/home/ost/test.py", line 32, in <module>
    app.start()
RuntimeError: disconnected

Solution

  • This question was answered in the libqi-python GitHub repository by nyibbang.

    TLDR: Use tcps in the URL instead of tcp and it should work.

    For the sake of completeness:

    On Pepper, the external TCP servers expect SSL/TLS connections so that communications are secure. This means that the server socket will expect a TLS handshake request from the client. To represent this expectation in libqi, the server address URL scheme becomes tcps instead of tcp (similarly to https vs http). If you target a tcps endpoint with a tcp address, the client socket will not request a handshake, causing the server to close the socket and your code to receive this "Socket disconnected" error.

    
    def newAuthenticator(self):
        return Authenticator(self.username, self.password)
     
    # Connect to the robot fails at app.start() => RuntimeError: disconnected
    # app = qi.Application(sys.argv, url="tcp://192.168.1.59:9503") OLD
    app = qi.Application(sys.argv, url="tcps://192.168.1.59:9503") # tcp changed to tcps
    logins = ("nao", "OMITTED")
    factory = AuthenticatorFactory(*logins)
    app.session.setClientAuthenticatorFactory(factory)