Open richardellwood opened 3 years ago
Historically by default the Unifi controller is on port 8443, not port 443. If you are running a CloudKey or similar then there is a UI on 443, but it redirects you to port 8443 for the Unifi Network interface and port 7443 for the Unifi Protect interface. Try the using port 8443 and see if that fixes it (or just don't specify the port
argument, since it defaults to the right port).
That said, even if that fixes it the error message is unhelpful; I'll put in some more informative error handling when I have some time.
Thank you, Nickovs. 8443 isn't working for me either. I'm going to try digging into your code and see if I can figure out what's going on. If I find a solution, I'll certainly contribute.
Based on the stack backtrace, it looks like the crash is happening because whatever the server is sending back can not be decoded as a JSON payload even though the HTTP request returned an OK
result. This is happening at line 110 in unifi.py
. This suggests to me that the endpoint that you are hitting is not the Unifi controller endpoint but something else (hence my suggestion that you check the port).
If you are up for trying to debug this yourself then you probably want to start by printing r.text
just before the code tries to extract the reply body as JSON, to see what is actually getting returned.
I've been running into this issue too, on the latest 1.8.5 firmware.
When I do the print
, the output is:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1"><link href="/2.css" rel="stylesheet"></head>
<body>
<div id="root"></div>
<script type="text/javascript" src="/vendor.0306759b.chunk.js"></script><script type="text/javascript" src="/main.d8e6c715.js"></script></body>
</html>
I had to edit metaprogram.py
and on L77 change:
return "https://{host}:{port}/api/s/{site}/{endpoint}{path}".format(
to:
return "https://{host}:{port}/proxy/network/api/s/{site}/{endpoint}{path}".format(
Likewise, in unifi.py
, L142 needs to change to:
"auth/login"
and L161 needs to change to:
"auth/logout"
Also in unifi.py
, L114, there is on response['data']
object any more. I'm not sure what it used to look like before, but now response
is just a hash full of lots of info. I assume that might be what we're looking for?
Sorry about the flood of comments, I don't know how you'd want to update the library to support the new firmware with all these changes and still be backwards compatible so I'm dumping this knowledge here :)
Ok, hopefully last update, this is a diff of what worked in the end, for both login and various other things. Happy to send a pull request if you want it as is, but it will break people on older firmware for sure: https://github.com/avleen/unificontrol/commit/0c9fcfaa19fe9671e12ac68de054f1e4b47d8e31
@avleen Thanks for all of these details. Can you confirm that you are also using a UDM Pro? What version of the server code are you running? I don't currently have access to a UDM Pro but if we can find a way to tell the difference and you are up for some testing then maybe we can build a version of this library that works with the UDM, the CloudKey and the stand-alone Java version.
Hi Nicko!
I have the UDM (non-pro) device, which has the built-in cloud key. I'm happy to do testing :)
On Thu, Jan 14, 2021 at 4:28 PM Nicko van Someren notifications@github.com wrote:
@avleen https://github.com/avleen Thanks for all of these details. Can you confirm that you are also using a UDM Pro? What version of the server code are you running? I don't currently have access to a UDM Pro but if we can find a way to tell the difference and you are up for some testing then maybe we can build a version of this library that works with the UDM, the CloudKey and the stand-alone Java version.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/nickovs/unificontrol/issues/12#issuecomment-760307578, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAEDXBKQ7ZGEQBDU2GWEO4DSZ4LS7ANCNFSM4TP6Y2HQ .
Hello i am running in the same issue . i installed this library with pip install git+git://github.com/avleen/unificontrol.git@0c9fcfaa19fe9671e12ac68de054f1e4b47d8e31
but still getting same error :(
Hello i am running in the same issue . i installed this library with
pip install git+git://github.com/avleen/unificontrol.git@0c9fcfaa19fe9671e12ac68de054f1e4b47d8e31
but still getting same error :(
What device and firmware version do you have @thundergreen ?
I am running a UDM Pro with firmware 1.8.5 - and willing to test. Though my python skills are very limited. The UDM Pro hosts multiple functions. Apart from the Network Controller there is also Protect, Access and Talk. Which is why the Network API endpoints are at /proxy/network/api/s and the authentication endpoints at /api/auth.
FYI, I'm also getting the same error with a UDM Base. The Network Controller is 6.0.43, UDM firmware is 1.8.5.
I"m not sure if this helps you detect UDM*, but uname -a
(from the UniFi OS shell) gives me:
Linux ubnt 4.1.37-v1.8.5.2964-30c04be #1 SMP Tue Dec 29 05:40:16 MST 2020 aarch64 GNU/Linux
Quite a few defaults seem to be different: I believe hostname is always unifi
(and default domain is .localdomain
), default port is 443
, default administrator is root
.
The root web application (https://unifi
) lets you choose between the Network Controller (URI /network
), or managing the UDM itself (/settings
).
The latest API, via curl
, is here: https://dl.ui.com/unifi/6.0.45-564eef1dda/unifi_sh_api
It would be helpful if someone (or even several people) with UDMs, CloudKeys and locally installed Java controllers could run:
import requests
print(requests.get("https://<unifi IP address>:8443/status", verify=False).text)
print(requests.get("https://<unifi IP address>:443/status", verify=False).text)
and send me the output.
To save clogging this discussion up, maybe paste the output into a Gist and just post the link here. I'm guessing that this might be enough to let me auto-detect what sort of controller the client is trying to talk to.
Hey Nicko,
At least on the 1.8.5 firmware on the UDM (non-pro), that URL doesn't work any more:
On Mon, Jan 25, 2021 at 10:28 PM Nicko van Someren notifications@github.com wrote:
It would be helpful if someone (or even several people) with UDMs, CloudKeys and locally installed Java controllers could run:
import requestsprint(requests.get("https://
:8443/status", verify=False).text)print(requests.get("https:// :443/status", verify=False).text) and send me the output.
To save clogging this discussion up, maybe paste the output into a Gist https://gist.github.com and just post the link here. I'm guessing that this might be enough to let me auto-detect what sort of controller the client is trying to talk to.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/nickovs/unificontrol/issues/12#issuecomment-767154850, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAEDXBPZGZOKTJFDE6YCGKLS3XV7HANCNFSM4TP6Y2HQ .
Can you try https://<unifi IP address>:443/proxy/network/status
?
Unauthenticated this doesn't work. Authenticated I get back:
{"meta":{"rc":"ok","uuid":"<uuid_stripped>"},"data":[]}
On Mon, Jan 25, 2021 at 10:50 PM Nicko van Someren notifications@github.com wrote:
Can you try https://
:443/proxy/network/status? — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/nickovs/unificontrol/issues/12#issuecomment-767163159, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAEDXBJGPVOQSCMU7TW7HYLS3XYUBANCNFSM4TP6Y2HQ .
Thanks. When you say "doesn't work", do you get a 401 result? If so, do you get something akin to:
{"meta":{"rc":"error","msg":"api.err.LoginRequired"},"data":[]}
in the body that is returned. Thanks.
Yes, unauthenticated, the UDM's /proxy/network/status endpoint returns a 401. Response body is simply Unauthorized
.
As verification of the response body, the response header contains:
Content-Length: 12
Content-Type: text/plain; charset=utf-8
...so, not JSON with useful information, just plain text.
@SinisterStairs Thanks for that, although it's not what I had hoped. What do you get if you try https://<unifi IP address>:443/api/auth/login
or https://<unifi IP address>:443/api/auth/login?username=xxxx&password=xxxx
? Do you get a similarly unhelpful response body or do you get JSON with error details?
Interestingly enough, I typo'd the auth login URL (I used /auth/login
instead of /api/auth/login
, and got a 200 using basic authentication (response text/html). I get hung up trying to pass in the username/password as query strings to /auth/login
.
Using EDIT: This was user error, with how I constructed my curl command./api/auth/login
gives me a JSON 401 when using basic authentication, and hangs trying to pass authentication via query strings.
UPDATE: Need sleep, getting sloppy. I lost of track when I used GET or POST, and when I used basic authentication or query strings. By "hung up" I mean no immediate response to curl, and I hit ctrl-c before timing out.
I have pushed a branch which hopefully will allow support for the Dream Machine as well as the "classic" controller. It's available in the udm-support
branch. There is a new server_type
argument to the client constructor; if you pass unificontrol.UNIFI_SERVER_UDM
then hopefully it should work with the UDM; it defaults to support the classic controller so as not to break existing code.
It would be a big help if some of you could give this a try and tell me if it works. I don't have a UDM so it's basically based on the code @avleen posted. If I manage to lay my hands on a UDM I'll also try to write some reliable auto-detection code but for the moment you'll need to tell it which you're using.
Thanks so much. I'm getting a unificontrol.exception.UnifiTransportError: 404: Not Found due to invalid credentials.
I tried my UDM's local account, my Unifi Cloud account, and the root SSH password.
https://gist.github.com/SinisterStairs/ab2ebbc208b5bcf34f15a3872811467a
EDIT: Will play around some more in a bit; just wanted to provide some quick feedback.
EDIT2: With curl I get a 405 using EDIT3: This was errors with my curl command./api/auth/login
and 200 using /auth/login
. Changing unifi.py to use /auth/login
didn't make a difference, however. WIth curl, I'm passing in username and password through basic authentication, not as query string parameters.
@SinisterStairs Thanks for trying it out. In the mean time I will see if I can find access to a UDM myself.
I tried to create a read-only user for you on my NC that could call the API w/o editing; but I don't think that's possible. If you're unable to find access to a UDM, maybe we could do a screen share sometime (e.g. Zoom). I'm on US Eastern time.
EDIT: I also wanted to thank you for your persistence, supporting a device you don't even own!
@SinisterStairs Can you try running the following code before you import unificontrol
or attempt to connect to your controller and post what log output you get?
import logging
logging.basicConfig(level=logging.DEBUG)
I edited both the gist's code and comment with the latest. https://gist.github.com/SinisterStairs/939d6a33521aed0e4e6ab36239e584f7
EDIT: To update my previous comments: Authenticating with /api/auth/login
is working via curl
; previous errors I reported were due to how I constructed the curl command or invoked the endpoint. My functioning curl command is in the comments within the gist above.
Thanks for the debug output. Can you try adding a call to client.login()
before your call to list_clients()
? Since the client uses a pooled HTTP/1.1 connection it's possible that the Unifi proxy somehow marks connection as having failed after the first 401 error thus doesn't accept the login retry.
I updated the gist with the code including client.login()
, and added the output as a new comment.
https://gist.github.com/SinisterStairs/939d6a33521aed0e4e6ab36239e584f7
Thanks. It looks like there are several issues here:
/api/auth/login
but the debug message suggests that it got a 200 reply from /auth/login
).The login and logout methods have always been a little anomalous compared to all of the other calls so I think that I will just need to have special case code to deal with these quirks. I will see what I can do.
FWIW, using curl to /api/auth/login
and /auth/login
give me different responses. The /api endpoint gives me the JSON already in the gist, while the /auth endpoint gives me HTML. I'll update the gist with the latter.
Did you edit the code at all before running the tests? Your last comment on the gist has a debug entry saying that it's sending POST /auth/login HTTP/1.1
but the source code in my latest branch says /api/auth/login
.
Good catch...at some point I edited unifi.py to see if it made any difference to try /api/auth/login
vs /auth/login
. I'll revert and re-run everything again.
EDIT: Gist Revision 2 results updated in the gist (and invalid one deleted).
Great. So that deals with issue (1) in my comment above. Issue (3) is a fairly easy fix. This still leaves the fact that the result returned by the UDM's authentication is different. It would be helpful if you could re-try the CURL authentication request and send me the complete JSON output (or at least all the keys, feel free to redact the values where it looks like private information). I can then try to write a new login()
method that supports both old and new styles.
EDIT: I deleted the old curl
results from the comments, then added up-to-date ones as comments.
https://gist.github.com/SinisterStairs/939d6a33521aed0e4e6ab36239e584f7
Also note, I am using my local NC account to authenticate (both python and curl), not Unifi Cloud account.
OK. I've pushed some fixes that might work for you. Please give them a try when you can.
Cheers! I raise my glass to you:
jMacPro15:~/src$ ./test.py
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): unifi.local:443
DEBUG:urllib3.connectionpool:https://unifi.local:443 "POST /api/auth/login HTTP/1.1" 200 4836
DEBUG:urllib3.connectionpool:https://unifi.local:443 "GET /proxy/network/api/s/default/stat/sta HTTP/1.1" 200 None
[{'site_id': 'SUID', 'assoc_time': 1612175493, 'latest_assoc_time': 1612211103, 'oui': 'Espressi', 'user_id': 'UUID', '_id': 'UID', 'mac': '00:00:00:FF:00:FF', ...
(The JSON was simply produced by print(str(clients))
, which is why there's a leading bracket. FYI, I left the client.login()
line still in the script, as shown in the gist's rev 2.)
Excellent! I'm glad you got it going.
When you have a moment if you could test a few bits it would be a big help so that I can clean this up and push a new version.
client.login()
)?client.stat_status()
call? It has a different, non-API path on the classic controller so I've no idea if the path is going to be the same on the UDM.client.logout()
works too then that would be good.I'll then fix up the documentation and push a new master branch and PyPI package.
client.login()
commented out
https://gist.github.com/SinisterStairs/939d6a33521aed0e4e6ab36239e584f7#gistcomment-3616078client.stat_status()
. If I get time, I'll try and see if /api/status
, or some derivative, works
https://gist.github.com/SinisterStairs/939d6a33521aed0e4e6ab36239e584f7#gistcomment-3616086client.logout()
. 405 Method Not Allowed, so it probably needs to be a POST instead of GET
https://gist.github.com/SinisterStairs/939d6a33521aed0e4e6ab36239e584f7#gistcomment-3616093Thanks. I thought those might fail! I think that I'm going to need to lay my hands on a UDM in order to debug this properly.
If you're willing to take a peek under the hood, one thing you can do is fire up the Developer Mode on your web browser and look at the network resources that are accessed. In there you should be able see if there is a "status" path that gets accessed and spot what method gets used on the login and logout calls.
Interesting, logout is indeed POST https://unifi.local/api/auth/logout
:
Logging into Unifi OS (https://unifi
):
Clicking on Network goes to the the Network Controller, Settings goes to UDM's UniFi Settings.
In order to sort this out I bought one of the stand-alone Dream Machine units and, after upgrading to the version 6 controller, I can now test this myself. As a result I have pushed a new version of code to the udm-support
branch. This properly supports the automatic login, so as long as you pass server_type=UnifiServerType.UDM
to the client constructor you should just be able to point it at the server and go.
Having said that, it appears that there are quite a few API endpoints that are very different in the new version of the controller. It's possible that I'll need to split out support for the UDM into a new client class so I've not merged this branch onto the master yet. If people with UDMs feel like testing various API calls and reporting their success or failure it would be a big help.
Hi... following this issue. I've been playing around with the udm-support branch and using Sinister's test script have been able to login and get client info.
Am happy to help you debug more of the library so that you can release the UDM support to master. Not super experienced with Python, but am with many other languages.
@stevehoek Many thanks for the offer of help. At this stage what I need most is for people to (a) test all the various calls that are there at the moment to see if they work correctly on the UDM and (b) identify any functionality in the newest UDM console that is not currently covered by the unificontrol
client.
@nickovs perfect, I will do that. Similar to what Sinister noted, the client.logout is not working even though your latest udm-support branch seems to be doing a POST /api/auth/logout
Here is what I get from my app I'm trying to integration your library in:
File "./pialert.py", line 513, in query_unifi_api unifi.logout() File "/home/pi/pialert/back/src/unificontrol/unificontrol/metaprogram.py", line 157, in wrapper return instance(client, *a, **kw) File "/home/pi/pialert/back/src/unificontrol/unificontrol/metaprogram.py", line 130, in call reply_format=self._reply_format) File "/home/pi/pialert/back/src/unificontrol/unificontrol/unifi.py", line 129, in _execute raise UnifiTransportError("{}: {}".format(resp.status_code, resp.reason)) unificontrol.exceptions.UnifiTransportError: 404: Not Found
sorry to ask - but how to clone the udm-branch version from GitHub
Hi @mjhcdk. Actually that's a perfectly reasonable question.
If you just want to install the test branch directly into a Python environment (regular or virtual) then you can run:
pip install git+https://github.com/nickovs/unificontrol.git@udm-support#egg=unificontrol
If you want to play with the source code as well then you should do something like:
git clone https://github.com/nickovs/unificontrol.git
cd unificontrol
git checkout udm-support
pip install --editable .
If you've previously installed the package and you now want to switch to using a local, editable copy you might need to change that last line to read:
pip install --force-reinstall --editable .
Thanks for clarification. At least for time being I will just go with install option.
However, at this time not sure what is causing the failure when running script...
EDIT: Tried with another function , and now I get successful feedback - Great
router = UnifiClient(host = "10.0.1.1", port = "443", username = UNIFI_USERNAME, password = UNIFI_PASSWORD,site='default',server_type=UnifiServerType.UDM)
clients = router.list_clients()
print (clients)
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): 10.0.1.1:443
DEBUG:urllib3.connectionpool:https://10.0.1.1:443 "GET /proxy/network/api/s/default/stat/sta HTTP/1.1" 401 12
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): 10.0.1.1:443
DEBUG:urllib3.connectionpool:https://10.0.1.1:443 "POST /api/auth/login HTTP/1.1" 200 1451
DEBUG:urllib3.connectionpool:https://10.0.1.1:443 "GET /proxy/network/api/s/default/stat/sta HTTP/1.1" 200 None
[{'site_id': '5e53b368f5399c04f8bb2ef1', 'assoc_time': 1612162028, 'latest_assoc_time': 1612663336, 'oui': 'Sonos', 'user_id': '5e53b8ddf5399c0538275953', '_id': '5e53b8ddf5399c0538275953', 'mac': '00:0e:58:f4:d0:fa', 'is_guest': False, 'first_seen': 1582545117, 'last_s
Original.
# Unifi.py
import sys
from unificontrol import UnifiClient
from unificontrol.constants import UnifiServerType
import requests
import json
import re
import logging
logging.basicConfig(level=logging.DEBUG)
UNIFI_USERNAME="xxx"
UNIFI_PASSWORD="xxx"
router = UnifiClient(host = "10.0.1.1", port = "443", username = UNIFI_USERNAME, password = UNIFI_PASSWORD,site='default',server_type=UnifiServerType.UDM)
guests = router.list_guests(within=24)
print (guests)
## end
Michaels-Mac:~ michael$ python3 unifi.py
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): 10.0.1.1:443
DEBUG:urllib3.connectionpool:https://10.0.1.1:443 "POST /proxy/network/api/s/default/stat/guest HTTP/1.1" 401 12
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): 10.0.1.1:443
DEBUG:urllib3.connectionpool:https://10.0.1.1:443 "POST /api/auth/login HTTP/1.1" 200 1451
DEBUG:urllib3.connectionpool:https://10.0.1.1:443 "POST /proxy/network/api/s/default/stat/guest HTTP/1.1" 404 9
Traceback (most recent call last):
File "/Users/michael/unifi.py", line 17, in <module>
guests = router.list_guests(within=24)
File "/usr/local/lib/python3.9/site-packages/unificontrol/metaprogram.py", line 157, in wrapper
return instance(client, *a, **kw)
File "/usr/local/lib/python3.9/site-packages/unificontrol/metaprogram.py", line 128, in __call__
return client._execute(url, method, rest_dict,
File "/usr/local/lib/python3.9/site-packages/unificontrol/unifi.py", line 129, in _execute
raise UnifiTransportError("{}: {}".format(resp.status_code, resp.reason))
unificontrol.exceptions.UnifiTransportError: 404: Not Found
Michaels-Mac:~ michael$
Thanks for the update. Finding these differences is exactly why I need people to test this!
Attempting to connect to my UDM Pro with the program
I get the following error
Any advice is appreciated. Richard