A python library for accessing the API

Hey all,

I’ve started to develop a python library for interacting with the HTTPS API via Python Requests.

At the moment, it connects, and it lists devices, but I intend to add all of the functionality that is documented in the SwaggerUI.

I’m writing it primarily to scratch an itch, however if anyone else would like to contribute you’re more than welcome.

The code is on Github at https://github.com/mockingbirdconsulting/pyloraserver, and all changes need to go through a code review before being merged, so hopefully this will end up being a relatively quality bit of code!

To preempt the question about why I’m not using gRPC for this, requests+TLS is an easier option for me!

2 Likes

If the Swagger UI supports outputting in YAML or another declarative format, there is automated client SDK tooling available (open source or commercial) that generates SDKs for a dozen+ languages. Whether or not those SDKs are idiomatic code is another issue…

1 Like

Though I like the idea, I agree with Brian: there are available Swagger clients for Python like https://github.com/Yelp/bravado, https://github.com/pyopenapi/pyswagger, etc., that make this kinds of unnecessary.

That said, I’ve developed a lot of unnecessary projects to learn, adjust things to my tiny unmet requirement or just for the sake of it. So I’ll be happy to give a hand whenever I can.

2 Likes

Yeah, so I’ve looked at the auto-gen libraries in the past for similar things, and they basically produce untested code that has really weird class names.

There’s also a the point that they don’t understand how the API actually works - for instance, in order to create a device in LoRaServer, you have to call make two separate calls to the API - one to create the device, one to set the keys - so in a “custom” library, we can have a single function that calls both endpoints.

I’m pretty sure that in future there would also be “extra” things that we might want to do (for example, iterate over the organisations and return the devices grouped by the gateway that last saw them or similar), so a custom library also helps with that.

1 Like

Hey folks, version 0.1.0 has been released and can be installed following the instructions at https://pypi.org/project/pyloraserver/

The documentation is pretty much non-existent right now, however as an example the following will create a device:

from pyloraserver import loraserver, device
cx = loraserver.Loraserver(
        loraserver_url="https://my.lora.server",
        loraserver_user="my_account",
        loraserver_pass="my_password")
d = device.Devices(loraserver_connection=cx)
d.description = "My LoRaWAN Device"
d.deveui = "deadbeefdeadbeef"
d.name = "ELSYS-ELT2-HP-DEMO"
d.profile_id = "profile-id-string-from-ui" # Documentation on Device profile searching will be useful here!
d.appid = appId # An INT specifiying the app id - again, you can search for this already but it's not documented!
d.nwkKey = "myNetworkKey" # If this is not specified, a random one will be generated for you
res = d.create_and_activate()
print(res)

All and any help on this library would be appreciated, it’s all fully tested (although I’ve not opened up access to the CI server itself, it just reports back to github), so please have a go and let me know what you think!

2 Likes

For those of you playing along at home, I’ve managed to write up some documentation which includes examples.

This is now live at https://pyloraserver.readthedocs.io/ and I’m using this library to add new devices to a LoRaServer.io cluster without any issues so far.

Bug reports/feature requests/contributions are all welcome :slight_smile:

And now it’s “officially” launched: https://www.mockingbirdconsulting.co.uk/blog/2019-04-24-announcing-pyloraserver/

2 Likes

Hello, I used the pyloraserver library, the routine code is the code in your documentation, just modified the IP address. The server does not set the user name so the default value is used. The result of the operation appears to be actively rejected by the server. Can you tell me what went wrong?
requests.exceptions.ConnectionError: HTTPSConnectionPool(host=‘192.168.222.111’, port=443): Max retries exceeded with url: /api/internal/login (Caused by NewConnectionError(’<urllib3.connection.VerifiedHTTPSConnection object at 0x000001189135AD30>: Failed to establish a new connection: [WinError 10061] Unable to connect because the target computer actively refused. '))

Hello,

Thanks for trying the library out, I suspect that there might be a firewall or similar that is refusing the connection on the server side.

Are you able to connect to port 443 on that host via a web browser or telnet?

I also notice that you have WinError 10061 in your logs. A quick search for this error suggests that it could be an issue with the server not running or Windows environment settings.

I don’t have a windows box to test this on, so I’m not sure how much more help I can be, but if you try the above and let me know the results then I’ll see what I can do to debug further.

@Proffalken
Traceback (most recent call last):
File “d:/dashpostweb/loraserver.py”, line 7, in
Loraserver_pass=""
File “C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\site-packages\pyloraserver\loraserver.py”, line 34, in init
Self.connect()
File “C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\site-packages\pyloraserver\loraserver.py”, line 64, in connect
Auth_header = self._authenticate()
File “C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\site-packages\pyloraserver\loraserver.py”, line 48, in _authenticate
Json=payload
File “C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\site-packages\requests\api.py”, line 116, in post
Return request(‘post’, url, data=data, json=json, **kwargs)
File “C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\site-packages\requests\api.py”, line 60, in request
Return session.request(method=method, url=url, **kwargs)
File “C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\site-packages\requests\sessions.py”, line 533, in request
Resp = self.send(prep, **send_kwargs)
File “C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\site-packages\requests\sessions.py”, line 646, in send
r = adapter.send(request, **kwargs)
File “C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\site-packages\requests\adapters.py”, line 516, in send
Raise ConnectionError(e, request=request)
requests.exceptions.ConnectionError: HTTPSConnectionPool(host=‘192.168.222.111’, port=443): Max retries exceeded with url: /api/internal/login (Caused by NewConnectionError(’<urllib3.connection.VerifiedHTTPSConnection object at 0x000001869E18AD30>: Failed to establish a new connection: [WinError 10061] due to target calculation
The machine refused actively and could not connect. '))
The port of the firewall is open, and if it is not open, it will prompt another kind of information.
Failed to establish a new connection: [WinError 10060] due to the connection party after a period of time
The host that did not reply correctly or connected did not respond and the connection attempt failed. '))

I come back to this error message.

Please can you answer the following:

  • Is the LoRaServer running on the ip address 192.168.222.111?
  • If it is, then is the LoRaServer listening on port 443?
  • Is there a TLS Certificate in place? If so, is it valid?
  • Can you access the web interface of LoRaServer?

The error handling could probably be better in the library, and I’ll create a github issue for that shortly, however from that stacktrace and what you’ve said so far, the issue isn’t in the library, it’s to do with your setup.

@Proffalken
Hello, thank you for your help:
1: The IP of my loraserver server is 192.168.222.111;
2: loraserver adopts the default installation, and the listening port has no special settings;
3: There is no TLS certificate.
4: The 8080 port of the test loraserver server can be https://accessed to the LORA-app-server interface, and the communication operation and connection of the system are normal. I can view the uplink and downlink data of the node.

ok, so that explains it then.

Configure the library as follows:

# Setup the connection
cx = loraserver.Loraserver(
        loraserver_url="http://192.168.222.111:8080",
        loraserver_user="my_api_username",
        loraserver_pass="my_api_password"
        )

Note that this removes the https and sets the port to 8080 in the loraserver_url setup (obviously you’ll need to update the username and password as well!).

Once this is done, the system should start working.

All our systems run behind NGinx and use letsencrypt.org to provide secure connections as this is “best practice” when providing services online.

I’m hoping to add more functionality to this library in the next week or so to provide the ability to add gateways etc, so I’d love to hear your thoughts over on github about what might be on your list of “next features” - https://github.com/mockingbirdconsulting/pyloraserver/issues is the place to post those.

@Proffalken
image
Hello, I follow the example you said, the program does not report an error, it can be completed normally, but there is no list of output devices. My appid has only 2, each id includes several devices, and there is no output device list for any one ID. It’s unknown for what reason?

Please can you log this as a bug at https://github.com/mockingbirdconsulting/pyloraserver/issues along with the code you are currently using and what you expect to see?

This needs to be tracked against the code base now as you may have found a bug.

Thanks :slight_smile:

1 Like

Downloaded your latest version of 0.16 and it works fine. thank you very much.

Thanks, and thanks for closing the Github issue as well!

Thank for the python package. I’ve been trying to get it to work this morning, but haven’t had a lot of success. I am guessing that the issue isn’t with your package itself, but I figured I would start here.

I am not using TLS and everything is stock. I made the changes based on your answer from July of last year, but the error I get is

python list_devs.py
Traceback (most recent call last):
File “list_devs.py”, line 7, in
chirpstack_pass=“admin”
File “C:\Python27\lib\site-packages\pychirp\chirpstack.py”, line 34, in init
self.connect()
File “C:\Python27\lib\site-packages\pychirp\chirpstack.py”, line 64, in connect
auth_header = self._authenticate()
File “C:\Python27\lib\site-packages\pychirp\chirpstack.py”, line 51, in _authenticate
jwt = auth_tok[‘jwt’]
KeyError: ‘jwt’

Isn’t the default credentials admin/admin?

TIA

Hi, this hasn’t got anywhere near as much love as I would have liked to have given it recently.

I’m hoping to work on it again in the very near future, could you log this as a bug report on Github please as it should work with admin/admin unless @brocaar is doing something really cool which prevents the API from being used with default credentials?

No worries, will do. I am still able to log into the webserver via admin/admin, but I know that that might be different than the API you are working through.