From 8ce264b2a00adc92108b42776be5135dc93a1b3f Mon Sep 17 00:00:00 2001 From: Joris van de Sande Date: Sun, 9 Aug 2015 21:06:47 +0200 Subject: [PATCH] Initial release of the Unifi API Client --- .gitignore | 4 + LICENSE | 19 ++++ README.md | 54 +++++++++ composer.json | 32 ++++++ examples/README.md | 28 +++++ examples/authorize-guest.php | 35 ++++++ examples/config.example.php | 11 ++ examples/device-statistics.php | 37 ++++++ examples/statistics.php | 37 ++++++ examples/unauthorize-guest.php | 35 ++++++ src/Client.php | 202 +++++++++++++++++++++++++++++++++ 11 files changed, 494 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 composer.json create mode 100644 examples/README.md create mode 100644 examples/authorize-guest.php create mode 100644 examples/config.example.php create mode 100644 examples/device-statistics.php create mode 100644 examples/statistics.php create mode 100644 examples/unauthorize-guest.php create mode 100644 src/Client.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5c7e8b0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea +/examples/config.php +/composer.lock +/vendor \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9a78d3d --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015 Joris van de Sande, https://github.com/jorisvandesande + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..43104e6 --- /dev/null +++ b/README.md @@ -0,0 +1,54 @@ +Unifi API Client +================ + +Unifi API Client can be used to connect to the API of your [Ubiquiti Unifi Controller](https://www.ubnt.com/enterprise/software/). +This client is build on top of [Guzzle](http://guzzlephp.org/). + +The code is tested against Unifi Controller version 4.6.6. + +Installation +------------ + +The API client can be installed with [Composer](https://getcomposer.org/): + + composer require jorisvandesande/unifi-api-client + +Or you can download the latest release at: + https://github.com/jorisvandesande/unifi-api-client/releases + + +Usage +----- + +```php +use JVDS\UnifiApiClient\Client; +use GuzzleHttp\Client as HttpClient; + +$apiClient = new Client(new HttpClient(['base_uri' => 'https://127.0.0.1:8443'])); +$apiClient->login('your_username', 'your_password'); + +// call supported methods via methods on the client +$apiClient->statistics('default'); + +// or call any API url via the get and post methods: +$apiClient->get('/api/self'); +$apiClient->post('/api/s/default/cmd/stamgr', ['cmd' => 'block-sta', 'mac':'01:01:01:01:01:01']) + +// logout +$apiClient->login('your_username', 'your_password'); +``` + +Examples can be found in the [examples](examples) directory. To run the examples, you must +copy the config.example.php file to config.php and change the configuration to your needs. + +Supported API calls +------------------- + +At the moment only a few API methods are implemented in the Client. Altough it is possible to use +the ```get()``` and ```post()``` methods of the Client to call any API url, the goal is to +support more methods. + +License +------- + +MIT Licensed, see the [LICENSE](LICENSE) file. \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..457ecaf --- /dev/null +++ b/composer.json @@ -0,0 +1,32 @@ +{ + "name": "jorisvandesande/unifi-api-client", + "type": "library", + "description": "Unifi API Client can be used to connect to the API of your Ubiquiti Unifi Controller", + "keywords": ["unifi", "ubiquiti", "api client", "api", "unifi controller", "client"], + "homepage": "https://github.com/jorisvandesande/unifi-api-client", + "license": "MIT", + "authors": [ + { + "name": "Joris van de Sande", + "email": "joris@vandesande.link", + "homepage": "https://github.com/jorisvandesande" + } + ], + "require": { + "php": ">=5.5.0", + "guzzlehttp/guzzle": "~6.0" + }, + "autoload": { + "psr-4": { + "JVDS\\UnifiApiClient\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "archive": { + "exclude": ["/examples"] + } +} diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..d134f80 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,28 @@ +Examples +======== + +To run the examples, you must copy the config.example.php file to config.php + and change the configuration to your needs. + +statistics.php +-------------- + +Fetches the client statistics for a given site. + + php statistics.php + +authorize-guest.php +------------------- + +Authorizes a guest (mac address) for x minutes. + You need to login with a user that has full access to the Unifi controller. + + php authorize-guest.php + +unauthorize-guest.php +--------------------- + +Unauthorize a guest (mac address). + You need to login with a user that has full access to the Unifi controller. + + php unauthorize-guest.php \ No newline at end of file diff --git a/examples/authorize-guest.php b/examples/authorize-guest.php new file mode 100644 index 0000000..0c9d815 --- /dev/null +++ b/examples/authorize-guest.php @@ -0,0 +1,35 @@ + $config['base_uri']])); + +try { + + // login to the unifi controller API + $apiClient->login($config['username'], $config['password']); + + // Authorize guest with mac address 01:01:01:01:01:01 for 60 minutes + // You need a user with full access to the unifi controller for this call! + $responseBody = $apiClient->authorizeGuest($config['site'], '01:01:01:01:01:01', 60); + + print_r(json_decode($responseBody)); + + $apiClient->logout(); + +} catch (RequestException $e) { + echo $e->getMessage() . PHP_EOL; + + echo '----- Request ------' . PHP_EOL; + echo $e->getRequest()->getBody()->getContents(); + echo PHP_EOL; + + echo '----- Response ------' . PHP_EOL; + echo $e->hasResponse() ? $e->getResponse()->getBody()->getContents() : '- no response -'; + echo PHP_EOL; +} \ No newline at end of file diff --git a/examples/config.example.php b/examples/config.example.php new file mode 100644 index 0000000..90b819a --- /dev/null +++ b/examples/config.example.php @@ -0,0 +1,11 @@ + 'https://127.0.0.1:8443', + // Your username (You should create a user with read only access to the Unifi controller) + 'username' => '', + // Your password + 'password' => '', + // The site name to run the examples against + 'site' => 'default' +]; \ No newline at end of file diff --git a/examples/device-statistics.php b/examples/device-statistics.php new file mode 100644 index 0000000..ad5dac8 --- /dev/null +++ b/examples/device-statistics.php @@ -0,0 +1,37 @@ + $config['base_uri']])); + +try { + + // login to the unifi controller API + $apiClient->login($config['username'], $config['password']); + + // Fetch device statistics for the given site + $responseBody = $apiClient + ->deviceStatistics($config['site']) + ->getBody() + ->getContents(); + + print_r(json_decode($responseBody, true)); + + $apiClient->logout(); + +} catch (RequestException $e) { + echo $e->getMessage() . PHP_EOL; + + echo '----- Request ------' . PHP_EOL; + echo $e->getRequest()->getBody()->getContents(); + echo PHP_EOL; + + echo '----- Response ------' . PHP_EOL; + echo $e->hasResponse() ? $e->getResponse()->getBody()->getContents() : '- no response -'; + echo PHP_EOL; +} \ No newline at end of file diff --git a/examples/statistics.php b/examples/statistics.php new file mode 100644 index 0000000..5bd2e30 --- /dev/null +++ b/examples/statistics.php @@ -0,0 +1,37 @@ + $config['base_uri']])); + +try { + + // login to the unifi controller API + $apiClient->login($config['username'], $config['password']); + + // Fetch statistics for the given site + $responseBody = $apiClient + ->statistics($config['site']) + ->getBody() + ->getContents(); + + print_r(json_decode($responseBody)); + + $apiClient->logout(); + +} catch (RequestException $e) { + echo $e->getMessage() . PHP_EOL; + + echo '----- Request ------' . PHP_EOL; + echo $e->getRequest()->getBody()->getContents(); + echo PHP_EOL; + + echo '----- Response ------' . PHP_EOL; + echo $e->hasResponse() ? $e->getResponse()->getBody()->getContents() : '- no response -'; + echo PHP_EOL; +} \ No newline at end of file diff --git a/examples/unauthorize-guest.php b/examples/unauthorize-guest.php new file mode 100644 index 0000000..42f2af8 --- /dev/null +++ b/examples/unauthorize-guest.php @@ -0,0 +1,35 @@ + $config['base_uri']])); + +try { + + // login to the unifi controller API + $apiClient->login($config['username'], $config['password']); + + // Revoke authorization for guest with mac address 01:01:01:01:01:01 + // You need a user with full access to the unifi controller for this call! + $responseBody = $apiClient->unauthorizeGuest($config['site'], '01:01:01:01:01:01'); + + print_r(json_decode($responseBody)); + + $apiClient->logout(); + +} catch (RequestException $e) { + echo $e->getMessage() . PHP_EOL; + + echo '----- Request ------' . PHP_EOL; + echo $e->getRequest()->getBody()->getContents(); + echo PHP_EOL; + + echo '----- Response ------' . PHP_EOL; + echo $e->hasResponse() ? $e->getResponse()->getBody()->getContents() : '- no response -'; + echo PHP_EOL; +} \ No newline at end of file diff --git a/src/Client.php b/src/Client.php new file mode 100644 index 0000000..a62f2d9 --- /dev/null +++ b/src/Client.php @@ -0,0 +1,202 @@ + + * use JVDS\UnifiApiClient\Client; + * use GuzzleHttp\Client as HttpClient; + * + * $apiClient = new Client(new HttpClient(['base_uri' => 'https://127.0.0.1:8443'])); + * $apiClient->login('your_username', 'your_password'); + * $apiClient->statistics('default'); // fetch statistics for default site + * + * + * Unifi controllers come with a self signed certificate by default. + * To verify that you're connecting to your own unifi controller, + * you should download your controller's certificate. + * And the verify option should be set to the path of the downloaded certificate: + * + * + * use JVDS\UnifiApiClient\Client; + * use GuzzleHttp\Client as HttpClient; + * + * $apiClient = new Client( + * new HttpClient(['base_uri' => 'https://127.0.0.1:8443']), + * ['verify' => '/your/unifi/cert.pem'] + * ); + * $apiClient->login('your_username', 'your_password'); + * + * + * It is also possible to use your own certificate in the unifi controller. + * More information about this is available at: http://wiki.ubnt.com/UniFi_FAQ#Custom_SSL_certificate + * + * @author Joris van de Sande + */ +class Client +{ + /** + * @var ClientInterface|null + */ + private $client; + + /** + * @var array + */ + private $requestOptions; + + /** + * @param ClientInterface $client + * @param array $requestOptions Guzzle request options that will be sent with every request + * + * @link http://docs.guzzlephp.org/en/latest/request-options.html + */ + public function __construct(ClientInterface $client, array $requestOptions = []) + { + $this->client = $client; + $this->requestOptions = $this->getRequestOptions($requestOptions); + } + + /** + * Login to the Unifi controller. + * You need to login before you can make other api requests. + * + * @param string $username username + * @param string $password password + * + * @throws GuzzleException in case of a login failure. + */ + public function login($username, $password) + { + $this->post( + '/api/login', + ['username' => $username, 'password' => $password, 'strict' => true] + ); + } + + /** + * @throws GuzzleException in case of a failure. + */ + public function logout() + { + $this->client->request('get', '/logout', ['allow_redirects' => false] + $this->requestOptions); + } + + /** + * @param string $site + * + * @return ResponseInterface + * @throws GuzzleException + */ + public function statistics($site) + { + return $this->get('/api/s/' . $site . '/stat/sta'); + } + + /** + * @param string $site + * + * @return ResponseInterface + * @throws GuzzleException + */ + public function deviceStatistics($site) + { + return $this->get('/api/s/' . $site. '/stat/device'); + } + + /** + * Authorize a guest by mac address. + * + * @param string $site + * @param string $mac the mac address of the guest to authorize. + * @param int $minutes number of minutes to authorize guest. + * @param array $data associative array with extra data, i.e. up (kbps), down (kbps), bytes (MB) + * + * @return ResponseInterface + * @throws GuzzleException + */ + public function authorizeGuest($site, $mac, $minutes, array $data = []) + { + return $this->post( + '/api/s/' . $site . '/cmd/stamgr', + [ + 'cmd' => 'authorize-guest', + 'mac' => $mac, + 'minutes' => $minutes + ] + $data + ); + } + + /** + * Unauthorize a guest by mac address. + * + * @param string $site + * @param string $mac + * + * @return ResponseInterface + */ + public function unauthorizeGuest($site, $mac) + { + + return $this->post( + '/api/s/' . $site . '/cmd/stamgr', + [ + 'cmd' => 'unauthorize-guest', + 'mac' => $mac + ] + ); + } + + /** + * @param string $url (relative) url to the api endpoint + * @param array $data data to be sent with the request. + * + * @return ResponseInterface + * @throws GuzzleException + */ + public function post($url, array $data = []) + { + return $this->client->request( + 'post', + $url, + ['json' => $data] + $this->requestOptions + ); + } + + /** + * @param string $url + * @param array $data + * + * @return ResponseInterface + * @throws GuzzleException + */ + public function get($url, array $data = []) + { + $requestOptions = $this->requestOptions; + + if ($data) { + $requestOptions['query'] = $data; + } + + return $this->client->request('get', $url, $requestOptions); + } + + private function getRequestOptions(array $defaultRequestOptions) + { + return array_merge( + [ + 'cookies' => new CookieJar(), + 'verify' => false + ], + $defaultRequestOptions + ); + } +}