[PROJECT] Driver for Unifi Network Controllers

If your implementation with sync HTTP works for you then I assume it will work for @bago, too. :+1:

I'll be watching closely to see what other interesting ideas you come up with for info to expose in your driver.

The driver packages I made for personal use are mainly for presence detection via device connections. Happy to share via Github if anyone is interested.

1 Like

If it works for me, I do not know it will work for @Bago because I cannot remember if he uses a UDMP or other (plus I have been working late, getting up early all week and it is starting to show...).

One thing the version I post this afternoon will now include is Alarms. Any active (not archived) alarm on the controller will now be displayed.

2 Likes

I use the USG-Pro-4 with Cloud Key. I’m hopeful. Thanks for all you do.

1 Like

@Bago... sorry, as soon as I saw your response... I know you have told me at least twice now. :deep sigh:

Updated Version(s):

  • UnifiAPI.groovy = 0.1.3

Change(s):

  • Switched Login to be using a synchronous httpPost command rather than the previous asynchttpPost. This should help it determine the correct cookie. Thanks @tomw!
  • Alarms are now polled. In the refresh the driver will now poll for any active (NOT ARCHIVED) alarms that the controller might have. It could poll all but that gets REALLY long really quickly. Right now it will put the message associated with the alarm into a list for attribute "Alarms" and post it as an event. If there are no active ones you will just get an empty list there.
  • Some minor tweaks/code cleanup.

Different Topic(s):

  • There are a number of commands I have tried out and have commented out at this time that are stored in the driver. Not sure how much value they are but if anyone is interested you can always uncomment one, run it, check over the log data (need debug or trace set) and decide if there is something useful to you there that could be made more important. These commands are:
  1. CheckClients = Obtains the ENTIRE client list and provides a massive amount of information.
  2. CheckAccountStatus = Obtains all the information on the controller about the user that is being used for login. This is also a pretty large amount but I found it to have little that I thought was of real value.
  3. RefreshUnifiDevices = Refreshes all unifi devices but also provides a massive amount of data in general
  4. RefreshOnlineClients = Refreshes data for clients currently online, providing a list of EVERY client online with a lot of data for them as well
  5. RefreshAllClients = Refresh data for all clients that have ever connected
  6. CurrentHealth = Similar to CurrentStats.
  7. CurrentSites = Extremely limited info.
  8. SysInfo = Gets general system information.
  9. RogueAP = Polls LOTS of data on rogue Access Points detected.
  • There is one command that is listed in some of the API resources online but does not work on my UDMP (gets a 404 - Not Found) response. It MAY have to do with the fact that the UDMP does not have wifi built in even though I have a UAP-LR connected (I thought maybe it had data provided by that). In any case, this command is SpectrumScan and is expected to scan the wifi spectrum. Not sure if it would work for someone with a UDM.
1 Like

That did it. Thanks.

It does retrieve info. But presence gives this:

`dev:16292020-12-11 04:15:55.845 pmerrorUniFi - Unauthorized for MACExists, login again

dev:16292020-12-11 04:59:57.149 pmerrorUniFi - Unauthorized for CheckAlarms, login again
`

Well... Better but not there yet. Need to look at what is different for those.

1 Like

You're getting 401 (unauthorized) errors after what looks like a successful login. Same symptom with @snell 's driver as well as mine that you tried. It's really weird.

There are a bunch of reports of people getting 401 errors even in the controller web interface with Cloud Key controllers, and the recommended troubleshooting step is to repair or compact the internal database: UniFi - Network Controller: Repairing Database Issues on the UniFi Controller – Ubiquiti Networks Support and Help Center

Seems maybe worth a try. Before you do that, is there anything in the logs on the controller that stands out?

I’ve never received any 401 errors using the web interface. I’ll compact for grins and giggles.

Yeah, I'll admit it's sort of :man_shrugging:

I planned on buying a cloud key eventually. You have me curious enough that maybe I'll just pull the trigger now and figure this out, unless @snell has any other ideas.

1 Like

@Bago: Only thing (besides what @tomw already mentioned) I can wonder about is the account permissions maybe. Do you have more than one account for it, and MAYBE (just maybe), the controller is blocking access to those specific things for the account you normally use?

No other logins associated with this controller.

Well, it was more wishful thinking than anything else.

From the "Art-of-Wifi" info, the main thing it says is that 401 is typically triggered if the cookie/token is not valid or has expired.

Looking at that further... I think I might have spotted something. It seems like SOME controllers also need that CSRF sent as part of the header as well. Not just the cookie. Maybe that is the error?

I will try building in a capture of it from the login and adding it into the headers just to try. Should have the updated version soon (I hope).

2 Likes

Updated Version(s):

  • UnifiAPI.groovy = 0.1.4

Change(s):

  • Now including the CSRF token that was received in the login response headers into all subsequent request headers just like the Cookie token. It appears to have no difference for my UDMP but maybe it will help some other cases. I did it based on the "X-CSRF-Token" response header... so if someone sees one in theirs that is labeled differently (the headers are all dumped to the Trace log if you really want to check), please let me know. You will need to do a Login command to obtain the CSRF.
2 Likes
[dev:1629](http://192.168.10.15/logs/past#dev1629)2020-12-12 11:33:17.102 am [error](http://192.168.10.15/device/edit/1629)UniFi - Unauthorized for PresenceCheck, login again

[dev:1629](http://192.168.10.15/logs/past#dev1629)2020-12-12 11:33:11.779 am [error](http://192.168.10.15/device/edit/1629)UniFi - Unauthorized for MACExists, login again

[dev:1629](http://192.168.10.15/logs/past#dev1629)2020-12-12 11:33:04.138 am [error](http://192.168.10.15/device/edit/1629)UniFi - Unauthorized for PresenceCheck, login again

[dev:1629](http://192.168.10.15/logs/past#dev1629)2020-12-12 11:33:03.758 am [error](http://192.168.10.15/device/edit/1629)groovyx.net.http.HttpResponseException: on line 228 (Login)

@Bago: Posting an updated version. I was already working on adding a Status message in to let people have a quick status of the driver in the Events and then saw your latest. Same error as before so obviously what I did had no effect. At least it does not sound like it made it worse. The new version might help figure out why though.

Updated Version(s):

  • UnifiAPI.groovy = 0.1.5

Change(s):

  • Added a Status attribute that should have a message telling if commands were successful and what the error was if they were not.
  • Added exception logging to the httpPost that is used for the Login command so maybe it will be possible to figure out what is going on in some cases.

@snell, I think your parsing of the csrf_token isn't quite right, unfortunately. It still comes in as a Set-Cookie header with csrf_token= in the value. I blame them for it being weird. :wink:

Here's how I did it. I get the value from the RHS of the csrf_token= and stuff it into my requests. It doesn't seem to make any difference whether it is there or not (including when I deliberately corrupt it), so I don't know if it will make a difference for @bago.

def cookie
def csrfTok
resp.getHeaders().each
{
    if((it.value.split('=')[0].toString() == "unifises") || (it.value.split('=')[0].toString() == "TOKEN"))
    {
        cookie = it.value.split(';')[0]
    }
    if(it.value.split('=')[0].toString() == "csrf_token")
    {
        csrfTok = it.value.split(';')[0].split('=')[1]
    }
}

setCookie(cookie)
setCsrf(csrfTok)


Then, later:

def params =
        [
            uri: getBaseURI() + getAPISuffix() + suffix,
            headers:
            [
                'Cookie': getCookie(),
                'X-CSRF-Token': getCsrf()
            ],
            ignoreSSLIssues: true,
        ]
[dev:1629](http://192.168.10.15/logs/past#dev1629)2020-12-13 08:15:02.533 am [error](http://192.168.10.15/device/edit/1629)UniFi - Exception when performing Login: groovyx.net.http.HttpResponseException:

[dev:1629](http://192.168.10.15/logs/past#dev1629)2020-12-13 08:15:01.714 am [debug](http://192.168.10.15/device/edit/1629)UniFi - Driver version up to date

[dev:1629](http://192.168.10.15/logs/past#dev1629)2020-12-13 08:15:01.270 am [trace](http://192.168.10.15/device/edit/1629)UniFi - Event: Last Refresh = Sun Dec 13 08:15:01 CST 2020

[dev:1629](http://192.168.10.15/logs/past#dev1629)2020-12-13 08:15:01.268 am [error](http://192.168.10.15/device/edit/1629)UniFi - No Cookie and/or CSRF available for authentication, must Login

[dev:1629](http://192.168.10.15/logs/past#dev1629)2020-12-13 08:15:01.267 am [error](http://192.168.10.15/device/edit/1629)UniFi - No Cookie and/or CSRF available for authentication, must Login

[dev:1629](http://192.168.10.15/logs/past#dev1629)2020-12-13 08:15:01.265 am [error](http://192.168.10.15/device/edit/1629)UniFi - No Cookie and/or CSRF available for authentication, must Login

[dev:1629](http://192.168.10.15/logs/past#dev1629)2020-12-13 08:15:01.239 am [info](http://192.168.10.15/device/edit/1629)UniFi - Updated

[dev:1629](http://192.168.10.15/logs/past#dev1629)2020-12-13 08:15:01.209 am [trace](http://192.168.10.15/device/edit/1629)UniFi - MACPresense List size = 1

[dev:1629](http://192.168.10.15/logs/past#dev1629)2020-12-13 08:15:01.208 am [trace](http://192.168.10.15/device/edit/1629)UniFi - Refresh rate: 5 minutes

Updated Version(s):

  • UnifiAPI.groovy = 0.1.6

Change(s):

  • Updated CSRF portion again to try to make it work. Basically a copy of the section that @tomw posted above (THANKS!). I had to separate it a bit though because the UDMP does not have it's CSRF structured like that, so I made it so UDMPs keep how I had it (which worked/works on mine) but has the other for other controllers.
  • Major overhaul to how Params are for the various different calls. Rather than have 90% of it be repeated every time I made a function that returns the needed parameters with the only variable being the path for that specific call. Seems to work well and saved a lot of lines.
  • Overhaul to how some basic checks were done for calls. At the beginning of every call I had it check to make sure the Site, Tokens, etc... existed before making the call just to prevent basic errors. Rather than have those (and their resulting log/Status messages) repeated over and over I made a separate function that is used to check. Between these two new functions it cut out 160 lines...

I'm attempting this on a UDM Base model. When I attempt to login i get the following error.

Everything seem to be working except this two below.

I have this error when I press "check Alarms"

dev:12142020-12-13 18:56:42.485 errorUnifi udm pro - Unauthorized for CheckAlarms please Login again

This error when "save preferences"

dev:12142020-12-13 18:56:42.452 errorUnifi udm pro - Unauthorized for RefreshAllClients please Login again

dev:12142020-12-13 18:56:51.090 traceUnifi udm pro - Event: Status = CheckAlarms Unauthorized, please Login again

dev:12142020-12-13 18:56:51.039 traceUnifi udm pro - Event: Status = RefreshAllClients Unauthorized, please Login again