Skip to content

Instantly share code, notes, and snippets.

@danricho
Last active July 30, 2022 12:59
Show Gist options
  • Save danricho/5db1dfde8bdac766ed79883e20d7a483 to your computer and use it in GitHub Desktop.
Save danricho/5db1dfde8bdac766ed79883e20d7a483 to your computer and use it in GitHub Desktop.
Unifi API Access
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# BASED ON ALCHEMYDC'S GREAT SCRIPT HERE:
# https://github.com/alchemydc/unifi_device_manager
#
# > ADDED FUNCTIONS FOR GETTING CLIENT LIST AND ACTIVE CLIENT LIST
# > HAD TO ADD "verify=False" TO ALL REQUESTS CALLS
# BECAUSE I HAVEN'T GOT A CERT SORTED IN UNIFI YET
# > TESTED ON CLOUD KEY GEN2 PLUS
# > FIRMARE: v2.0.27
# > NETWORK CONTROLLER UI: v6.0.44
# > NETWORK CONTROLLER BACKEND: v6.0.45
import requests
import json
USERNAME = "<unifi local username>"
PASSWORD = "<unifi local password>"
BASEURL = "<unifi url>"
SITE = "<unifi site>"
# THIS IS USED TO DETERMINE IF SOMEONE IS HERE.
MOBILE_PHONES = { # "MAC" : "NAME"
"aa:bb:cc:dd:ee:ff": "Son",
"ff:ee:dd:cc:bb:aa": "Daughter",
}
# constants
LOGINURL = BASEURL + '/api/auth/login'
LOGOUTURL = BASEURL + '/api/auth/logout'
# create a custom requests object, modifying the global module throws an error
http = requests.Session()
assert_status_hook = lambda response, *args, **kwargs: response.raise_for_status()
http.hooks["response"] = [assert_status_hook]
#setup Requests to log request and response to stdout verbosely
http = requests.Session()
# set required headers
http.headers.update({
"Content-Type": "application/json; charset=utf-8"
})
def login():
creds = json.dumps({'username': USERNAME,'password': PASSWORD})
response = http.post(LOGINURL, data=creds, verify=False)
# new API requires the CSRF token going forward else we get 401's or 404's
http.headers.update({
"X-CSRF-Token": response.headers['X-CSRF-Token']
})
def logout():
response = http.post(LOGOUTURL)
def toggleAP(guid, action):
"""
Toggles an AP into 'disabled' mode and back. Perhaps useful for reducing RF exposure during sleep.
Note the guid is NOT the MAC addr of the AP.
To get the guid call FIXME $baseurl/api/s/$site/stat/device
"""
APURL = BASEURL + '/proxy/network/api/s/' + SITE + '/rest/device/' + guid
if action == "enable":
disableFlag = False
elif action == "disable":
disableFlag = True
payload = json.dumps({'disabled': disableFlag})
response = http.put(APURL, data = payload, verify=False)
def listUsers(hours=8760):
"""
Gets a list of all clients in the given time period.
"""
APURL = BASEURL + '/proxy/network/api/s/' + SITE + '/stat/alluser'
payload = json.dumps({'type': 'all', 'conn': 'all', 'within': int(hours)})
response = dict(http.put(APURL, data = payload, verify=False).json())
return response["data"]
def activeUsers():
"""
Gets a list of clients currently connected.
"""
APURL = BASEURL + '/proxy/network/api/s/' + SITE + '/stat/sta/'
payload = json.dumps({})
response = dict(http.put(APURL, data = payload, verify=False).json())
return response["data"]
def main():
login()
allUsers = listUsers()
activeClients = activeUsers()
logout()
userDict = {}
print("\n------------------------------------------------------")
print("ALL USERS (LAST 365 DAYS)")
print("------------------------------------------------------")
for user in allUsers:
if "name" in user:
print(user["mac"], "alias: " + user["name"])
elif "hostname" in user:
print(user["mac"], "hostname: " + user["hostname"])
else:
print(user["mac"],"!!!unknown!!!")
userDict[user["mac"]] = user
print("\n------------------------------------------------------")
print("PHONES")
print("------------------------------------------------------")
for user in activeClients:
if user["mac"] in userDict:
userDict[user["mac"]]["online"] = True
for mac in MOBILE_PHONES:
if mac in userDict:
if "online" in userDict[mac]:
print(MOBILE_PHONES[mac] + " IS here.")
else:
print(MOBILE_PHONES[mac] + " IS NOT here.")
else:
print(MOBILE_PHONES[mac] + "? Never heard of 'em!")
print()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment