Git Product home page Git Product logo

unificontrol's Introduction

A high-level Python interface to the Unifi controller software

unificontrol is a rich and full-featured Python interface to the Ubiquiti Unifi software defined network controller. Goals of this package include:

  • A clean interface that supports introspection and self-documentation.
  • A complete implementation of the Unifi controller API. Currently it supports over 100 API calls to the Unifi controller.
  • Proper handling of SSL connections to allow secure access to the controller even when the controller uses a [self-signed certificate](ssl_self_signed.md).
  • A concise, readable internal representation of the Unifi API, so that new API calls can easily be added as new features are added to the controller.
  • Python 3 only, since it's the way of the future.

Installation

To install the most recent release use:

pip install unificontrol

To install the latest version of the code from GitHub use:

pip install -e git+https://github.com/nickovs/unificontrol.git@master#egg=unificontrol

Documentation

The unificontrol code aims to be self-documenting as far as possible so if you are using it in an interactive environment the built in Python help() function will often tell you what you need.

There is also documentation that can be built using Sphynx in the docs directory and a built version of these docs is hosted on ReadTheDocs.

Usage

The simplest way to use this client is simply to create an instance with the necessary parameters and log in:

client = UnifiClient(host="unifi.localdomain",
    username=UNIFI_USER, password=UNIFI_PASSWORD, site=UNIFI_SITE)

The host name (and the host port, if you are using something other than the default 8443) must be specificed when you create the client. The username and password can be passed to the login method instead of the contstructor if you prefer. If you supply then username and password in the constructor then the client will automatically log in when needed and re-authenticate if your session expires.

Once you have created a client object you can simply make calls to the various API endpoints on the controler:

# Get a list of all the guest devices for the last day
guests = client.list_guests(within=24)

# Upgrade one of the access points
client.upgrade_device("11:22:33:44:55:66")

See the :any:`API documentation <API>` for full details.

Support for self-signed certificates

Since the Unifi controller uses a :any:`self-signed certifcate <ssl_self_signed>` the default behaviour of the client is to fetch the SSL certificate from the server when you create the client instance and pin all future SSL connections to require the same certificate. This works OK but if you are building some tool that will talk to the controller and you have place to store configuration then a better solution is to store a copy of the correct certificate in a safe place and supply it to the constructor using the cert keyword argument. A server's certifcate can be fetched using the python ssl library:

import ssl
cert = ssl.get_server_certificate(("unifi.localdomain", 8443))
# Store the cert in a safe place
...
# Fetch the cert from a safe place
client = UnifiClient(host="unifi.localdomain",
    username=UNIFI_USER, password=UNIFI_PASSWORD, site=UNIFI_SITE,
    cert=cert)

If you have a proper certificate for the controller, issued by a known authority and with a subject name matching the host name used to access the server then you can switch off the certificate pinning by passing cert=None.

Acknowledgments

I would almost certainly never have written such a complete implementation of the API had it not been for the hard work done by the authors of the PHP Unifi API client created by Art of WiFi. While the code here was written from scratch, all of the necessary analysis and understanding of the undocumented API was taken from the PHP client. Without that open source project I would probably have stopped with less than a quarter of the API finished.

unificontrol's People

Contributors

kdknigga avatar nickovs avatar timbersson avatar wolph avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

unificontrol's Issues

SSL Verification Errors

I'm trying to get a basic script working that just does a connection. Problem is that it does not like my SSL cert on my UDM. It claims that the cert has expired and will not connect. This does not seem to be true. I can view the cert in the browser or print & decode the cert from the script and see the following.

How can I make this work?

Certificate Information:
Common Name: unifi.local
Subject Alternative Names: unifi.local, localhost, [::1], IP Address:127.0.0.1, IP Address:FE80:0:0:0:0:0:0:1
Valid From: September 13, 2022
Valid To: December 16, 2024
Serial Number: 77bfabe473a0d81532

My code:
import unificontrol
client = unificontrol.UnifiClient(host="192.168.10.1", username="myusername", password="my password", site="default")

The error I'm getting is:

Traceback (most recent call last):
File "C:\Users\wesw\AppData\Local\Programs\Python\Python39\lib\site-packages\urllib3\connectionpool.py", line 703, in urlopen
httplib_response = self._make_request(
File "C:\Users\wesw\AppData\Local\Programs\Python\Python39\lib\site-packages\urllib3\connectionpool.py", line 386, in _make_request
self._validate_conn(conn)
File "C:\Users\wesw\AppData\Local\Programs\Python\Python39\lib\site-packages\urllib3\connectionpool.py", line 1042, in validate_conn
conn.connect()
File "C:\Users\wesw\AppData\Local\Programs\Python\Python39\lib\site-packages\urllib3\connection.py", line 414, in connect
self.sock = ssl_wrap_socket(
File "C:\Users\wesw\AppData\Local\Programs\Python\Python39\lib\site-packages\urllib3\util\ssl
.py", line 453, in ssl_wrap_socket
ssl_sock = ssl_wrap_socket_impl(sock, context, tls_in_tls)
File "C:\Users\wesw\AppData\Local\Programs\Python\Python39\lib\site-packages\urllib3\util\ssl
.py", line 495, in _ssl_wrap_socket_impl
return ssl_context.wrap_socket(sock)
File "C:\Users\wesw\AppData\Local\Programs\Python\Python39\lib\ssl.py", line 500, in wrap_socket
return self.sslsocket_class._create(
File "C:\Users\wesw\AppData\Local\Programs\Python\Python39\lib\ssl.py", line 1040, in _create
self.do_handshake()
File "C:\Users\wesw\AppData\Local\Programs\Python\Python39\lib\ssl.py", line 1309, in do_handshake
self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:1122)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "C:\Users\wesw\AppData\Local\Programs\Python\Python39\lib\site-packages\requests\adapters.py", line 489, in send
resp = conn.urlopen(
File "C:\Users\wesw\AppData\Local\Programs\Python\Python39\lib\site-packages\urllib3\connectionpool.py", line 787, in urlopen
retries = retries.increment(
File "C:\Users\wesw\AppData\Local\Programs\Python\Python39\lib\site-packages\urllib3\util\retry.py", line 592, in increment
raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='192.168.10.1', port=8443): Max retries exceeded with url: /api/s/default/stat/guest (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:1122)')))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "D:\dev\src\tools\unifi\test.py", line 10, in
clients = client.list_guests(within=24)
File "d:\src\unificontrol\unificontrol\metaprogram.py", line 125, in wrapper
return instance(client, *a, **kw)
File "d:\src\unificontrol\unificontrol\metaprogram.py", line 103, in call
return client._execute(url, self._method, rest_dict, need_login=self._need_login)
File "d:\src\unificontrol\unificontrol\unifi.py", line 96, in _execute
resp = ses.send(ses.prepare_request(request))
File "C:\Users\wesw\AppData\Local\Programs\Python\Python39\lib\site-packages\requests\sessions.py", line 701, in send
r = adapter.send(request, **kwargs)
File "C:\Users\wesw\AppData\Local\Programs\Python\Python39\lib\site-packages\requests\adapters.py", line 563, in send
raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='192.168.10.1', port=8443): Max retries exceeded with url: /api/s/default/stat/guest (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:1122)')))

UnifiTransportError: 400 when calling create_vouchers

Build: atag_5.11.39_12706
Vers: 5.11.39

when calling client.create_voucher() filling in kwargs with desired settings, it returns a 400 error with the traceback:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/barkern/.local/lib/python3.8/site-packages/unificontrol/metaprogram.py", line 125, in wrapper
    return instance(client, *a, **kw)
  File "/home/barkern/.local/lib/python3.8/site-packages/unificontrol/metaprogram.py", line 103, in __call__
    return client._execute(url, self._method, rest_dict, need_login=self._need_login)
  File "/home/barkern/.local/lib/python3.8/site-packages/unificontrol/unifi.py", line 115, in _execute
    raise UnifiTransportError("{}: {}".format(resp.status_code, resp.reason))
  unificontrol.exceptions.UnifiTransportError: 400: 

Any ideas? I am able to run other commands successfully such as client.list_sites() and client.stat_sysinfo(), but I am not sure if I am just not understanding the formatting of the function correctly or if it's been deprecated and doesn't work as intended any more.

Thanks!

Feature request

Hi, currently I’m using get_client_details method to receive data for client specified by mac.

But in my case i didn’t receive data that i need, specifically Port information per client.

I attached screenshot about what i mean.

Is it possible to parse it with your tool ?

404 on client login

I get a 404 error when I try to login the.
self.client.login()
unificontrol.exceptions.UnifiTransportError: 404:

Changing device settings with set_device_settings_base API

Hi there,

Many thanks for the creation of these APIs to work with UNIFI devices. Helps a lot with scripting.

I am trying to understand the use of set_device_settings_base API because I want to change the name property of a device in a site. For example, when I configure the device via the Cloud UI, I can add a device alias in the General tab under device configs.

Then, when I do:

print(client.list_devices_basic())

I see, the alias I added through the Cloud UI appear in the output. Here is an example of before and after changing the device name on CloudUI.
Before changing alias in device Config tab:

[{'mac': '00:00:00:00:00:00', 'state': 1, 'adopted': True, 'disabled': False, 'type': 'usw', 'model': 'US24'}]

After changing alias in device Config tab:

[{'mac': '00:00:00:00:00:00', 'state': 1, 'adopted': True, 'disabled': False, 'type': 'usw', 'model': 'US24', 'name': 'NEW_UNIFI_SWITCH1'}]

I intend to change the name field or add a name if the field does not exist. I think set_device_settings_base seems to be the correct API for it, but I don't seem to be able to figure out the settings payload. I would really appreciate some help here.

Thanks!

Unable to connect to a UDM Pro with this library

Attempting to connect to my UDM Pro with the program

from unificontrol import UnifiClient
import ssl

cert = ssl.get_server_certificate(("10.1.0.1", 443))

router = UnifiClient(host = "10.1.0.1", port = "443", username = "admin", password = "[password]", cert = cert)

I get the following error

Traceback (most recent call last):
  File "test.py", line 8, in <module>
    clients = router.list_sites()
  File "/home/sysop/.local/lib/python3.5/site-packages/unificontrol/metaprogram.py", line 123, in wrapper
    return instance(client, *a, **kw)
  File "/home/sysop/.local/lib/python3.5/site-packages/unificontrol/metaprogram.py", line 101, in __call__
    return client._execute(url, self._method, rest_dict, need_login=self._need_login)
  File "/home/sysop/.local/lib/python3.5/site-packages/unificontrol/unifi.py", line 110, in _execute
    response = resp.json()
  File "/home/sysop/.local/lib/python3.5/site-packages/requests/models.py", line 898, in json
    return complexjson.loads(self.text, **kwargs)
  File "/usr/lib/python3.5/json/__init__.py", line 319, in loads
    return _default_decoder.decode(s)
  File "/usr/lib/python3.5/json/decoder.py", line 339, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib/python3.5/json/decoder.py", line 357, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 2 column 1 (char 1)

Any advice is appreciated.
Richard

No way to use the library at all with self-signed certificate on the UDM Pro

Despite what the ``ssl_self_signed.rst` document states, pinning does not work at all for the initial login. You can create the client object, but you can't use it to query the API of the UDMP at all.

This includes port 443 and 8443. The self-signed certificate is queried correctly and passed back in the request, but since the Python 'requests' library can't validate the SSL certificate, it fails with the below (common) SSL verify error:

Traceback (most recent call last):
  File "./test.py", line 14, in <module>
    client.login()
  File "/tmp/env/src/unificontrol/unificontrol/unifi.py", line 155, in login
    self._login(username=username if username else self._user,
  File "/tmp/env/src/unificontrol/unificontrol/metaprogram.py", line 125, in wrapper
    return instance(client, *a, **kw)
  File "/tmp/env/src/unificontrol/unificontrol/metaprogram.py", line 103, in __call__
    return client._execute(url, self._method, rest_dict, need_login=self._need_login)
  File "/tmp/env/src/unificontrol/unificontrol/unifi.py", line 96, in _execute
    resp = ses.send(ses.prepare_request(request))
  File "/tmp/env/lib/python3.8/site-packages/requests/sessions.py", line 655, in send
    r = adapter.send(request, **kwargs)
  File "/tmp/env/lib/python3.8/site-packages/requests/adapters.py", line 514, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='192.168.x.x', port=443): Max retries exceeded with url: /api/login (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate (_ssl.c:1123)')))

Other than procuring an officially-signed, upstream SSL certificate that the standard host certificate chain will trust, this will not work. I did add the certificate directly from the controller to /usr/local/share/ca-certificates/ and refreshed the cert chain, still no success.

Is there a plan to support verify=False in the constructor, so this will work for those of us without an upstream SSL cert configured on our controllers?

create_radius_account leaving password blank

I'm trying to auto provision radius assigned vlan using MAC address. Calling create_radius_account with username and password being same results in blank password. Any suggestions?

UnifiTransportError after FW upgrade

Hi @nickovs,

Thank You for this tool, it worked perfectly for me, but today i updated FW on my Cloud Key Gen2 and now I’m receiving and error unificontrol.exceptions.UnifiTransportError: 404:
Here is my Client initialisation
def get_client():
cert = ssl.get_server_certificate((host, 8443))
return UnifiClient(host=host,
username=username, password=password, site="default", cert=cert)

Can you please help me to solve this issue ?

API extension to enable or disable firewall port forwarding rules

Hello,

I'm writing an API definition to support enabling or disabling firewall port forwarding rules. It sort of works, but I'm not sure how to debug with the metaprogramming stuff going on and was wondering if my problem is obvious.

I've added the following to unifi.py:

    enable_portforwarding_rule = UnifiAPICall(
        """Enable or disable a port forwarding rule

        Args:
            pfr_id (str): ``_id`` value of the portforwarding rule
            enabled (bool): true to enable the rule, false to disable the rule
        """,
        "rest/portforward",
        path_arg_name="pfr_id",
        path_arg_optional=False,
        json_args=["enabled"],
        method="PUT",
        )

Calling enable_portforwarding_rule(pfr_id="_id of portforwarding rule here", enabled=True) works! With requests/http.client debugging turned on I see b'{"enabled": true}' get sent to the correct place and the rule gets enabled.

However, when I call enable_portforwarding_rule(pfr_id="_id of portforwarding rule here", enabled=False), well, that doesn't work. The JSON that gets sent is b'{}'.

Is something getting eaten somewhere in the metaprogramming guts?

Thanks,

Kris

Attempts to retrieve get get 401 errors.

Have run into an issue getting unificontrol up and running and am not sure if this is a common issue or if I'm just not doing something right. However I create the object, I wind up getting a Transport Error with a 401 code back.

I'd created a limited read-only user in Unifi for this, and on the chance that was breaking it, have tried using my regular super admin, but that doesn't work either, same issue. Instead I get the same error.

import unificontrol client = UnifiClient(host=<my Cloudcontroller>,username=<my regular user>,password=<my regular password>,site='Default') devices = client.list_devices()

And result:

Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.6/site-packages/unificontrol/metaprogram.py", line 123, in wrapper return instance(client, *a, **kw) File "/usr/local/lib/python3.6/site-packages/unificontrol/metaprogram.py", line 101, in __call__ return client._execute(url, self._method, rest_dict, need_login=self._need_login) File "/usr/local/lib/python3.6/site-packages/unificontrol/unifi.py", line 115, in _execute raise UnifiTransportError("{}: {}".format(resp.status_code, resp.reason)) unificontrol.exceptions.UnifiTransportError: 401:

The implication here is that the login isn't actually working or is being rejected for some reason, but unclear to me why. Note that the UnifiClient call itself does not error out - I only the error once I call list_devices (or any other call to get data). Same result if I call UnifiClient without user info and then call login explicitly with that info.

I can hack some python but am not enough of an expert to know all the ins and outs of what might be up here. Appreciate any guidance. Best.

Create Radius Profile

Hello, I see a way to list radius profiles and create radius accounts. However I don't see a way to update or create a radius profile? Is that possible with this module?

SSL error when trying to use any function except the initial connection

Hi there.

If I run only:

import unificontrol
import ssl

cert = ssl.get_server_certificate(('hostname', 8443))
controller = unificontrol.UnifiClient(host='hostname', username='support', password='passoword', cert=cert, port=8443)

Then it runs and exits with no errors.

But as soon as I write a new line, for example:

controller.list_sites()

I get this error:
requests.exceptions.SSLError: HTTPSConnectionPool(host='hostname', port=8443): Max retries exceeded with url: /api/self/sites (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate in certificate chain (_ssl.c:1123)')))

It does this with any function I enter

***I didn't write the actual hostname, username, and password in this post, but in the code they are 100% correct

Install is broken due to missing requests

The setup.py includes unificontrol which (after a few more imports) also imports requests.

I'm guessing this could be solved by specifying requests as setup_requirements instead of install_requirements but not entirely sure.

Obtaining unificontrol
    ERROR: Command errored out with exit status 1:
     command: unificontrol/.venv/bin/python3.8 -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'unificontrol/setup.py'"'"'; __file__='"'"'unificontrol/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info
         cwd: unificontrol/
    Complete output (9 lines):
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "unificontrol/setup.py", line 4, in <module>
        import unificontrol
      File "unificontrol/unificontrol/__init__.py", line 30, in <module>
        from .unifi import UnifiClient, FETCH_CERT
      File "unificontrol/unificontrol/unifi.py", line 31, in <module>
        import requests
    ModuleNotFoundError: No module named 'requests'
    ----------------------------------------
ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.

To reproduce:

git clone https://github.com/nickovs/unificontrol.git
cd unificontrol
pipenv install -e .

trying to poe cycle a port..

>>> client.stat_sysinfo()
[{'timezone': 'America/New_York', 'autobackup': False, 'build': 'atag_8.2.93_25939', 'version': '8.2.93', 'previous_version': '8.1.127', 'data_retention_days': 90, 'data_retention_time_in_hours_for_5minutes_scale': 24, 'data_retention_time_in_hours_for_hourly_scale': 168, 'data_retention_time_in_hours_for_daily_scale': 2160, 'data_retention_time_in_hours_for_monthly_scale': 8760, 'data_retention_time_in_hours_for_others': 2160, 'update_available': False, 'update_downloaded': False, 'live_chat': 'super-only', 'store_enabled': 'super-only', 'hostname': '80211.domain-here', 'name': '80211.domain-here', 'ip_addrs': ['172.16.0.96', '10.20.0.4', '10.20.1.105'], 'inform_port': 8080, 'https_port': 8443, 'portal_http_port': 8880, 'override_inform_host': False, 'image_maps_use_google_engine': False, 'radius_disconnect_running': False, 'facebook_wifi_registered': False, 'sso_app_id': '8< -- SNIP -->8', 'sso_app_sec': '8< -- SNIP -->8', 'uptime': 829081, 'anonymous_controller_id': '8< -- SNIP -->8', 'has_webrtc_support': True, 'debug_setting_preference': 'auto', 'debug_mgmt': 'warn', 'debug_system': 'warn', 'debug_device': 'warn', 'debug_sdn': 'warn', 'unsupported_device_count': 0, 'unsupported_device_list': [], 'unifi_go_enabled': False, 'default_site_device_auth_password_alert': False}]

seems to be working..

>>> client.power_cycle_switch_port(e063da8a3052,31)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'e063da8a3052' is not defined

>>> client.power_cycle_switch_port('e063da8a3052','31')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "~/.local/lib/python3.12/site-packages/unificontrol/metaprogram.py", line 125, in wrapper
    return instance(client, *a, **kw)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/.local/lib/python3.12/site-packages/unificontrol/metaprogram.py", line 103, in __call__
    return client._execute(url, self._method, rest_dict, need_login=self._need_login)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/.local/lib/python3.12/site-packages/unificontrol/unifi.py", line 115, in _execute
    raise UnifiTransportError("{}: {}".format(resp.status_code, resp.reason))
unificontrol.exceptions.UnifiTransportError: 400:

image

Reuse unificontrol.UnifiClient when iterating sites on same controller

Currently if we "login" to unifi controllers and then get a list of sites to iterate for more details , it looks like a new instance of unificontrol.UnifiClient needs to be created each time in the loop. So for example, if there are 100 sites on a controller and we iterate them and an API for each site, there are 100 "logins" to the controller API created.
For example if we iterate a list of controller, extract list of sites from each one, and then attempt to device level detail of each .
Can there be a way to create an instance of unificlient without if calling a login to controller as we've already been authenticated in the first "cont" loop . Just trying to reduce the amount of logins required and load on unfi controller..

controllers =[ ["myserver.acme.com","8443","admin","mysecretpassword1"],"myserver2.acme.com","8443","admin","mysecretpassword2"]]

for cont in controllers:
login = unificontrol.UnifiClient(host=cont[0],port=cont[1],username=cont[2], password=cont[3], site="default",cert=None)

sites = login.list_sites()

for site in sites:
    current_site = site.get("name")
    current_sitedesc = site.get("desc")
    client = unificontrol.UnifiClient(host=cont[0],port=cont[1],username=cont[2], password=cont[3], site=current_site,cert=None)
    devices=  client.list_devices()

    for device in devices:
        ip =  device.get("ip")
        device_name = device.get("name")
        print (current_sitedesc , " " , device_name, " " , ip)


Provide a dataclass abstraction for clients and other objects

Currently the representation of clients in unificontrol is just the raw data that comes back from the underlying API. In a comment on another issue @wolph suggested that it would be useful to have an abstraction that represents clients as their own data objects and provide a higher level API for manipulating them.

Can't get most of the calls working

So in the meantime I switched to a Debian VM instance just installed in order to get familiarized with the API. It is running in Python 3.11.2 venv.
At first I could not get the SSL verification passing (CloudKey 7.2.97). So I applied this fix and then it passed.
The first call used work with no issues

from unificontrol import UnifiClient
from datetime import timedelta
import json
import requests
from urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)

controller = UnifiClient(host='192.168.21.221', username='xx', password='yyyyyy', site='SaintPierre', cert=None)
sites = controller.list_sites()
print(sites)

Then I tried added various API calls without any success. I tried to set username and password again, no luck.
For instance

controller.login()
devices= controller.list_devices_basic()

triggers an exception

Traceback (most recent call last):
  File "/home/debian/environments/unificontroller/test.py", line 13, in <module>
    devices= controller.list_devices_basic()
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/debian/environments/unificontroller/lib/python3.11/site-packages/unificontrol/metaprogram.py", line 125, in wrapper
    return instance(client, *a, **kw)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/debian/environments/unificontroller/lib/python3.11/site-packages/unificontrol/metaprogram.py", line 103, in __call__
    return client._execute(url, self._method, rest_dict, need_login=self._need_login)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/debian/environments/unificontroller/lib/python3.11/site-packages/unificontrol/unifi.py", line 115, in _execute
    raise UnifiTransportError("{}: {}".format(resp.status_code, resp.reason))
unificontrol.exceptions.UnifiTransportError: 401:

If I understand well there is a loggin issue. However I'm 100% sure of the credentials.
What could be wrong then?
Thank you

trying to create wlan networks

I'm trying to generate a few wifi networks.
In my limited python knowledge i've copied and modified the create_network to:

create_wlan = UnifiAPICall(
    "Create a wifi network",
    "rest/wlanconf",
    json_body_name='settings',
    method="POST",
    )

Now when i try to create a test network i get the following error:

Traceback (most recent call last):
File "<pyshell#188>", line 1, in
client.create_wlan(settings={'name': 'test1200_Office', 'usergroup_id': '60f9488e46e0fb012814562b', 'wlangroup_id': '60f9488e46e0fb012814562c', 'enabled': True, 'hide_ssid': False, 'is_guest': False, 'security': 'wpapsk', 'wpa_mode': 'wpa2', 'wpa_enc': 'ccmp', 'vlan_enabled': True, 'uapsd_enabled': False, 'x_passphrase': 'supergeheimwachtwoord', 'schedule_enabled': False, 'schedule': [], 'vlan': 10})
File "C:\Users\marijn\AppData\Roaming\Python\Python39\site-packages\unificontrol\metaprogram.py", line 125, in wrapper
return instance(client, *a, **kw)
File "C:\Users\marijn\AppData\Roaming\Python\Python39\site-packages\unificontrol\metaprogram.py", line 103, in call
return client._execute(url, self._method, rest_dict, need_login=self._need_login)
File "C:\Users\marijn\AppData\Roaming\Python\Python39\site-packages\unificontrol\unifi.py", line 115, in _execute
raise UnifiTransportError("{}: {}".format(resp.status_code, resp.reason))
unificontrol.exceptions.UnifiTransportError: 400:

Any ideas what i'm doing wrong?

Thanks

SSL Error?

Why am I getting SSL errors when I try to make the first connection?
When cert param isn't supplied at all:
OpenSSL.SSL.Error: [('system library', 'fopen', 'Broken pipe'), ('BIO routines', 'BIO_new_file', 'system lib'), ('x509 certificate routines', 'X509_load_cert_crl_file', 'system lib')]

When cert=None is set:
requests.exceptions.SSLError: HTTPSConnectionPool(host='192.168.2.126', port=8443): Max retries exceeded with url: /api/s/default/stat/sta (Caused by SSLError(SSLError("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')])")))

calling logout() results in error 400

Hey,

for periodic requests to the unifi controller I made a script that logs in, fetches the required data and logs out. Everything works up to calling client.logout(), then I get the following:

Traceback (most recent call last):
File "test.py", line 12, in
client.logout()
File "/home/sensor_bot/.local/share/virtualenvs/sensor_bot-ejTj32CH/lib/python3.7/site-packages/unificontrol/metaprogram.py", line 125, in wrapper
return instance(client, *a, **kw)
File "/home/sensor_bot/.local/share/virtualenvs/sensor_bot-ejTj32CH/lib/python3.7/site-packages/unificontrol/metaprogram.py", line 103, in call
return client._execute(url, self._method, rest_dict, need_login=self._need_login)
File "/home/sensor_bot/.local/share/virtualenvs/sensor_bot-ejTj32CH/lib/python3.7/site-packages/unificontrol/unifi.py", line 115, in _execute
raise UnifiTransportError("{}: {}".format(resp.status_code, resp.reason))
unificontrol.exceptions.UnifiTransportError: 400:

Running the unifi controller on a raspberry pi, controller version: 6.0.45. Python 3.7

Anyone able to connect to CloudKey with 6.0.43

Version 6 has similar changes as the UDM Pro, where the admin is now on port 443, and some of the endpoints are tweaked. I gave this only 1 minute, and it wasn't able to connect. I figure I would ask before spending time troubleshooting it. Does this work with 6.0 backends?

UniFiOS API now very slow since UDM firmware 1.11

I've been using the UDMsupport branch of this library in my project and it has been working great, up to and including UniFiOS 1.10
However, since upgrading this week to 1.11 the API calls are all taking much longer to return... device list, client list, client detail.

Is anyone else experiencing this?

Wildcard SSL on controller

I'm trying to connect to a (docker) controller using the library but getting stuck at the first hurdle.

I have a wildcard cert installed on the controller, which presents no issues when connecting via a web browser (GlobalSign root, AlphaSSL issuing server), but I receive the SSLError(SSLError("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')])")) when trying to use the library.

I saw closed issue fixed by running pip install pip_system_certs, which made no difference.

I ran the following test to eliminate the library:

import requests
response = requests.get("https://unifi-fqdn:8443/")
print(response)

<Response [200]>

I then added a temporary entry of unifi.local in my /etc/hosts file pointing at the same IP as above, which unsurprisingly results in an error:

requests.exceptions.SSLError: HTTPSConnectionPool(host='unifi.local', port=8443): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, "[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Hostname mismatch, certificate is not valid for 'unifi.local'. (_ssl.c:1131)")))

So it suggests to me that python requests at least is able to validate the controller SSL cert, but it's failing with the library.

I wondered whether the problem is cause by me using a wildcard certificate, I would appreciate it if anyone could confirm they are using the a wildcard cert successfully with the unificontrol library?

Certificate pinning error on Windows

On Windows, atttempting to use the api results in an exception for any call:

>>> unifi = UnifiClient(host="unifi",username=UNIFI_USER,password=UNIFI_PASSWORD)
>>> sites = unifi.list_sites()
Traceback (most recent call last):
  File "...\NetworkUtils\lib\site-packages\urllib3\util\ssl_.py", line 336, in ssl_wrap_socket
    context.load_verify_locations(ca_certs, ca_cert_dir)
PermissionError: [Errno 13] Permission denied

The cause of this is that on Windows a NamedTemporaryFile - which is used to store the pinned certificate - cannot be re-opened, and so the permission error is thrown when the ssl library tries to load the pinned certificate.

From python docs here:

Whether the name can be used to open the file a second time, while the named temporary file is still open, varies across platforms (it can be so used on Unix; it cannot on Windows NT or later).

It's possible to work around this by setting delete=False when creating the NamedTemporaryFile, although in that case the temporary file needs to be cleaned up by the code that creates it. I'm happy to provide a PR that does this.

No way to use library with unifi controller

Hello,
I'm running Unifi Controller 7.2.97-18705-1 on a CloudKey (firmware v1.1.19). Self signed certificate
This program run on a Windows 10 machine with Python 3.12.0

from unificontrol import UnifiClient
client = UnifiClient(host='xxx.xxx.xxx.xxx', username='xxx', password='yyyyyy')

triggers the following exception

Exception ignored in: <function PinningHTTPSAdapter.__del__ at 0x000001DB23C41580>
Traceback (most recent call last):
  File "C:\Users\NicolasRabaté\AppData\Roaming\Python\Python312\site-packages\unificontrol\pinned_requests.py", line 85, in __del__
  File "C:\Program Files\Python312\Lib\tempfile.py", line 478, in __getattr__
AttributeError: '_io.TextIOWrapper' object has no attribute 'delete'

What could be the reason? Thank you

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.