├── .DS_Store ├── .gitignore ├── LICENSE ├── README.md └── src └── Unms.php /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MonSansFil/unms/HEAD/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | node_modules/ 3 | npm-debug.log 4 | 5 | # Laravel 4 specific 6 | bootstrap/compiled.php 7 | app/storage/ 8 | 9 | # Laravel 5 & Lumen specific 10 | public/storage 11 | public/hot 12 | storage/*.key 13 | .env.*.php 14 | .env.php 15 | .env 16 | Homestead.yaml 17 | Homestead.json 18 | 19 | # Rocketeer PHP task runner and deployment package. https://github.com/rocketeers/rocketeer 20 | .rocketeer/ 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 MonSansFil 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## UNMS API client class 2 | 3 | A PHP class which provides access to Ubiquiti's **UNMS API**, versions 2.1 of the UNMS Controller software is supported. It's a standalone version of the class which is used in our monsansfil.ca project which is a private project. 4 | 5 | 6 | ## Methods and functions supported 7 | 8 | The class currently supports the following functions/methods to get/post/put/delete data through the UNMS API: 9 | 10 | - login() 11 | - logout() 12 | - create_site() 13 | - editSiteName() 14 | - getSites() 15 | - setSite() 16 | - authorizeDevice() 17 | - getAircubeWirelessConfig() 18 | - getAircubeNetworkConfig() 19 | - getAircubeSystemConfig() 20 | - setCubeLedOn() 21 | - setCubeName() 22 | - setCubeResetEnabled() 23 | - setCubePoeEnabled() 24 | - setAircubeSystemConfig() 25 | - setAircubeNetworkConfig() 26 | - setAircubeWirelessConfig() 27 | - getDevices() 28 | - getRouterInterfaces() 29 | - getAirCubeData() 30 | - getAirCubeDevices() 31 | - geteRouterDHCPLeases() 32 | - getAircubeWirelessInfo() 33 | 34 | 35 | Internal functions, getters/setters: 36 | 37 | - set_debug() 38 | - get_debug() 39 | - get_last_results_raw() 40 | - get_last_error_message() 41 | 42 | Please refer to the source code for more details on the functions/methods and their parameters. 43 | 44 | ## Requirements 45 | 46 | - a web server with PHP and cURL modules installed (tested on apache2 with PHP Version 7.2.2 and cURL 7.45.0 ) 47 | - network connectivity between this web server and the server and port (normally TCP port 443) where the UNMS Controller is running 48 | 49 | ## Installation ## 50 | 51 | You can use [Git](#git) or simply [Download the Release](#download-the-release) to install the API client class. 52 | 53 | 54 | ### Git 55 | 56 | Execute the following `git` command from the shell in your project directory: 57 | 58 | ```sh 59 | git clone https://github.com/MonSansFil/unms.git 60 | ``` 61 | 62 | When git is done cloning, include the file containing the class like so in your code: 63 | 64 | ```php 65 | require_once('path/to/src/Unms.php'); 66 | ``` 67 | 68 | ### Download the Release 69 | 70 | If you prefer not to use composer or git, you can simply [download the package](https://github.com/Art-of-WiFi/UniFi-API-client/archive/master.zip), uncompress the zip file, then include the file containing the class in your code like so: 71 | 72 | ```php 73 | require_once('path/to/src/Unms.php'); 74 | ``` 75 | 76 | ## Example usage 77 | 78 | A basic example how to use the class: 79 | 80 | ```php 81 | 82 | /** 83 | * initialize the Unms API connection class, log in to the controller and request the devices from a site 84 | * (this example assumes you have already assigned the correct values to the variables used) 85 | */ 86 | $unms_connection = new Unms($user, $password, $url, true); 87 | $login = $unms_connection->login(); 88 | $results = $unms_connection->getDevices($site_id); // returns a PHP array containing devices of the site 89 | ``` 90 | 91 | 92 | #### IMPORTANT NOTES: 93 | 94 | 1. The last parameter (`true`) that is passed to the constructor, enables validation of the controller's SSL certificate which is otherwise **disabled** by default. It is highly recommended to enable this feature in production environments where you have a valid SSL cert installed on the Unms Controller, and which is associated with the FQDN of the server as used in the `baseurl` parameter. 95 | 96 | 2. In the example above, `$site_id` is the id of the site which is visible in the URL when managing the site in the Unms Controller: 97 | 98 | `https://:443/sites/bb44fac2-3fb6-440d-bd37-f70202bcaf0f/devices` 99 | 100 | In this case, `bb44fac2-3fb6-440d-bd37-f70202bcaf0f` is the value required for $site_id. 101 | 102 | ## Need help or have suggestions? 103 | 104 | There is still work to be done to add functionality and improve the usability of this class, so all suggestions/comments are welcome. Please use the github [issue](https://github.com/MonSansFil/unms/issues) list to share your ideas/questions. 105 | 106 | ## Contribute 107 | 108 | If you would like to contribute code (improvements), please open an issue and include your code there or else create a pull request. 109 | 110 | ## Credits 111 | 112 | This class is based on the work done by the following developers: 113 | - Malle-pietje: https://github.com/Art-of-WiFi/UniFi-API-client 114 | 115 | ## Important Disclaimer 116 | 117 | Many of the functions in this API client class are not officially supported by UBNT and as such, may not be supported in future versions of the Unms Controller API. -------------------------------------------------------------------------------- /src/Unms.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * This Unms API client is based on the work done by the following developers: 10 | * Malle-pietje: https://github.com/Art-of-WiFi/UniFi-API-client 11 | * 12 | * Copyright (c) 2018, MonSansFil 13 | * 14 | * This source file is subject to the MIT license that is bundled 15 | * with this package in the file LICENSE.md 16 | */ 17 | 18 | 19 | /** 20 | * the Unms API client class 21 | */ 22 | class Unms 23 | { 24 | protected $baseurl = 'myhost.com:443'; 25 | protected $debug = false; 26 | protected $is_loggedin = false; 27 | private $token = ''; 28 | private $request_type = 'POST'; 29 | private $last_error_message = null; 30 | private $curl_ssl_verify_peer = false; 31 | private $curl_ssl_verify_host = false; //TODO use it in the init curl 32 | 33 | 34 | /** 35 | * Constructor of the API 36 | * @param string $user The username used to connect to the UNMS Api 37 | * @param string $password The account password 38 | * @param string $baseurl The url of the UNMS server, without the https:// and with the port at the end ex: unms.myhost.com:443 39 | * @param boolean $ssl_verify Wether or not use the ssl verify peer and host 40 | */ 41 | function __construct($user, $password, $baseurl = '', $ssl_verify = false) 42 | { 43 | if (!extension_loaded('curl')){ 44 | trigger_error('The PHP curl extension is not loaded. Please correct this before proceeding!'); 45 | } 46 | 47 | $this->user = trim($user); 48 | $this->password = trim($password); 49 | 50 | if (!empty($baseurl)) $this->baseurl = trim($baseurl); 51 | 52 | if ($ssl_verify === true){ 53 | $this->curl_ssl_verify_peer = true; 54 | $this->curl_ssl_verify_host = 2; 55 | } 56 | 57 | $this->check_base_url(); 58 | } 59 | 60 | /** 61 | * Destructor of the API. Will disconnect the user if not logged out manually 62 | */ 63 | function __destruct() 64 | { 65 | /** 66 | * logout, if needed 67 | */ 68 | if ($this->is_loggedin) $this->logout(); 69 | } 70 | 71 | /** 72 | * Set debug mode 73 | * -------------- 74 | * sets debug mode to true or false, returns false if a non-boolean parameter was passed 75 | * @param boolean $enable true will enable debug mode, false will disable it 76 | */ 77 | public function set_debug($enable) 78 | { 79 | if ($enable === true || $enable === false) { 80 | $this->debug = $enable; 81 | return true; 82 | } 83 | 84 | trigger_error('Error: the parameter for set_debug() must be boolean'); 85 | return false; 86 | } 87 | 88 | /** 89 | * Create a site or client in UNMS 90 | * @param array $site array containing all or some of theses parameters 91 | * integer parentSiteId The parentSiteId if you want to create a client 92 | * string name The name of the site, as displayed in the UNMS panel 93 | * string address The address of the site, as displayed in the UNMS panel 94 | * object location The object location 95 | * string contactName The contact name, as displayed in the UNMS panel 96 | * string contactPhone The contact phone number, as displayed in the UNMS panel 97 | * string contactEmail The contact email address, as displayed in the UNMS panel 98 | * string note The contact note, as displayed in the UNMS panel 99 | * integer height The site height above ground, as displayed in the UNMS panel 100 | * integer elevation The site elevation, as displayed in the UNMS panel. Will be calculatd automatically if nothing is present 101 | * 102 | * @return object The return value from the API 103 | */ 104 | public function create_site($site) 105 | { 106 | if (!$this->is_loggedin) return false; 107 | 108 | $defaultSite = [ 109 | 'parentSiteId' => null, 110 | 'name' => 'Undefined', 111 | 'address' => 'Undefined', 112 | 'location'=> null, 113 | 'contactName' => null, 114 | 'contactPhone' => null, 115 | 'contactEmail' => null, 116 | 'note' => null, 117 | 'height' => 8, 118 | 'elevation' => 8, 119 | ]; 120 | 121 | $site = array_merge($defaultSite,array_intersect_key($site, $defaultSite)); 122 | 123 | 124 | $json = json_encode([ 125 | 'parentSiteId' => $site['parentSiteId'], 126 | 'name' => $site['name'], 127 | 'address' => $site['address'], 128 | 'location' => $site['location'], 129 | 'contactName' => $site['contactName'], 130 | 'contactPhone' => $site['contactPhone'], 131 | 'contactEmail' => $site['contactEmail'], 132 | 'note' => $site['note'], 133 | 'height' => $site['height'], 134 | 'elevation' => $site['elevation'], 135 | 'location' => $site['location'], 136 | ]); 137 | 138 | $response = $this->exec_curl('/v2.1/sites', $json); 139 | return $this->process_response($response); 140 | } 141 | 142 | /** 143 | * Will edit the site name, without changing any other things 144 | * @param string $siteId The site ID to edit 145 | * @param string $name THe new name to be setted 146 | * 147 | * @return object The return value from the API or FALSE if an error occured 148 | */ 149 | public function editSiteName($siteId, $name) 150 | { 151 | $site = $this->getSites($siteId); 152 | if(!$site) return false; 153 | 154 | $site = $site[0]; 155 | 156 | $site->identification->name = $name; 157 | 158 | return $this->setSite($siteId, $site); 159 | } 160 | 161 | /** 162 | * Return the site's object 163 | * @param string $siteId The site id to get 164 | * @return object The entire site's object 165 | */ 166 | public function getSites($siteId) 167 | { 168 | if (!$this->is_loggedin) return false; 169 | $response = $this->exec_curl('/v2.1/sites?id='.$siteId); 170 | return $this->process_response($response); 171 | } 172 | 173 | /** 174 | * The set site function is only to be used by subfunctions in this class. It is expecting to receive a valid site object as parameter 175 | * @param string $siteId the site id to set 176 | * @param object $data the valid data of the site to set 177 | * 178 | * @return object The return value from the API or FALSE if an error occured 179 | */ 180 | protected function setSite($siteId, $data) 181 | { 182 | if (!$this->is_loggedin) return false; 183 | $json = json_encode($data); 184 | 185 | $this->request_type = 'PUT'; 186 | 187 | $response = $this->exec_curl('/v2.1/sites/'.$siteId, $json); 188 | return $this->process_response($response); 189 | } 190 | 191 | /** 192 | * Move a device from one site to the other 193 | * @param string $deviceId The device to move 194 | * @param string $siteId The site id in which to move the device 195 | * @return object The return value from the API or FALSE if an error occured 196 | */ 197 | public function authorizeDevice($deviceId, $siteId) 198 | { 199 | if (!$this->is_loggedin) return false; 200 | $json = json_encode(['siteId' => $siteId]); 201 | 202 | $response = $this->exec_curl('/v2.1/devices/'.$deviceId.'/authorize', $json); 203 | return $this->process_response($response); 204 | } 205 | 206 | /** 207 | * Get the Wireless configuration from an aircube 208 | * @param string $aircubeId The aircube ID to get the data from 209 | * @return object The return value from the API or FALSE if an error occured 210 | */ 211 | public function getAircubeWirelessConfig($aircubeId) 212 | { 213 | if (!$this->is_loggedin) return false; 214 | $response = $this->exec_curl('/v2.1/devices/aircubes/'.$aircubeId.'/config/wireless'); 215 | return $this->process_response($response); 216 | } 217 | 218 | /** 219 | * Get the Network configuration from an aircube 220 | * @param string $aircubeId The aircube ID to get the data from 221 | * @return object The return value from the API or FALSE if an error occured 222 | */ 223 | public function getAircubeNetworkConfig($aircubeId) 224 | { 225 | if (!$this->is_loggedin) return false; 226 | $response = $this->exec_curl('/v2.1/devices/aircubes/'.$aircubeId.'/config/network'); 227 | return $this->process_response($response); 228 | } 229 | 230 | /** 231 | * Get the System configuration from an aircube 232 | * @param string $aircubeId The aircube ID to get the data from 233 | * @return object The return value from the API or FALSE if an error occured 234 | */ 235 | public function getAircubeSystemConfig($aircubeId) 236 | { 237 | if (!$this->is_loggedin) return false; 238 | $response = $this->exec_curl('/v2.1/devices/aircubes/'.$aircubeId.'/config/system'); 239 | return $this->process_response($response); 240 | } 241 | 242 | /** 243 | * Set the aircube led On or Off 244 | * @param string $aircubeId The aircube ID to set the led 245 | * @param boolean $is_on Wether to turn On or Off the aircube led 246 | * @return object The return value from the API or FALSE if an error occured 247 | */ 248 | public function setCubeLedOn($aircubeId, $is_on) 249 | { 250 | $originVal = $this->getAircubeSystemConfig($aircubeId); 251 | if(!$originVal) return false; 252 | 253 | $origin = (array) $originVal; 254 | 255 | $origin['ledNightMode'] = (array) $origin['ledNightMode']; 256 | 257 | $origin['ledNightMode']['enable'] = ($is_on ? false : true);; 258 | $origin['ledNightMode']['start'] = "0"; 259 | $origin['ledNightMode']['end'] = "0"; 260 | 261 | return $this->setAircubeSystemConfig($aircubeId, $origin); 262 | } 263 | 264 | /** 265 | * Set the aircube name 266 | * @param string $aircubeId The aircube ID to set the name 267 | * @param string $name The new name to be setted to the cube 268 | * @return object The return value from the API or FALSE if an error occured 269 | */ 270 | public function setCubeName($aircubeId, $name) 271 | { 272 | $originVal = $this->getAircubeSystemConfig($aircubeId); 273 | if(!$originVal) return false; 274 | 275 | $origin = (array) $originVal; 276 | 277 | $origin['ledNightMode'] = (array) $origin['ledNightMode']; 278 | 279 | $origin['deviceName'] = $name; 280 | 281 | return $this->setAircubeSystemConfig($aircubeId, $origin); 282 | } 283 | 284 | /** 285 | * Set the aircube reset swith available or not 286 | * @param string $aircubeId The aircube ID to set the name 287 | * @param string $is_enabled Wether or not the reset switch is available 288 | * @return object The return value from the API or FALSE if an error occured 289 | */ 290 | public function setCubeResetEnabled($aircubeId, $is_enabled) 291 | { 292 | $originVal = $this->getAircubeSystemConfig($aircubeId); 293 | if(!$originVal) return false; 294 | 295 | $origin = (array) $originVal; 296 | $origin['ledNightMode'] = (array) $origin['ledNightMode']; 297 | 298 | $origin['resetButtonEnabled'] = ($is_enabled ? true : false); 299 | 300 | return $this->setAircubeSystemConfig($aircubeId, $origin); 301 | } 302 | 303 | /** 304 | * Set the aircube POE On or Off 305 | * @param string $aircubeId The aircube ID to set the name 306 | * @param string $is_enabled Wether or not the POE is turned On 307 | * @return object The return value from the API or FALSE if an error occured 308 | */ 309 | public function setCubePoeEnabled($aircubeId, $is_enabled) 310 | { 311 | $originVal = $this->getAircubeSystemConfig($aircubeId); 312 | if(!$originVal) return false; 313 | 314 | $origin = (array) $originVal; 315 | $origin['ledNightMode'] = (array) $origin['ledNightMode']; 316 | 317 | $origin['poePassthrough'] = ($is_enabled ? true : false); 318 | 319 | return $this->setAircubeSystemConfig($aircubeId, $origin); 320 | } 321 | 322 | /** 323 | * Set the aircube system configuration 324 | * @param string $aircubeId The aircube ID to set the config 325 | * @param array $config array containing all or some of theses parameters 326 | * deviceName The name of the aircube, as displayed in the UNMS panel 327 | * timezone The timezone of the aircube, as displayed in the UNMS panel 328 | * zonename Not used, for future use 329 | * ledNightMode 330 | * enable Wether or not the nightMode is enables 331 | * start The time to start the nightmode every day 332 | * end The time to end the nightmode every day 333 | * resetButtonEnabled Wether or not the reset Button is enabled on the cube 334 | * poePassthrough Wether or not the POE is enabled on the cube 335 | * username The Administrator username 336 | * newPassword The administrator password 337 | * 338 | * @return object The return value from the API 339 | */ 340 | public function setAircubeSystemConfig($aircubeId, $config) 341 | { 342 | if (!$this->is_loggedin) return false; 343 | $defaultConfig = [ 344 | "deviceName" => "AirCube", 345 | "timezone" => "UTC", 346 | "zonename" => null, 347 | "ledNightMode" => [ 348 | "enable" => false, 349 | "start" => "0", 350 | "end" => "0", 351 | ], 352 | "resetButtonEnabled" => false, 353 | "poePassthrough" => false, 354 | "username" => $this->user, 355 | "newPassword" => $this->password, 356 | ]; 357 | 358 | $config = array_merge($defaultConfig,array_intersect_key($config, $defaultConfig)); 359 | $config['ledNightMode'] = array_merge($defaultConfig['ledNightMode'],array_intersect_key($config['ledNightMode'], $defaultConfig['ledNightMode'])); 360 | 361 | $json = json_encode($config); 362 | 363 | $this->request_type = 'PUT'; 364 | 365 | $response = $this->exec_curl('/v2.1/devices/aircubes/'.$aircubeId.'/config/system', $json); 366 | 367 | return $this->process_response($response); 368 | } 369 | 370 | 371 | /** 372 | * Set the aircube network configuration 373 | * @param string $aircubeId the aircube ID to set the config 374 | * @param array $config the configuration array (see UNMS doc for details. Upcoming here in future version) 375 | * 376 | * @return object The return value from the API 377 | */ 378 | public function setAircubeNetworkConfig($aircubeId, $config) 379 | { 380 | if (!$this->is_loggedin) return false; 381 | $defaultConfig = [ 382 | "mode" => (isset($config['mode']) ? $config['mode'] : "bridge"), 383 | "blockManagementAccess" => (isset($config['blockManagementAccess']) ? $config['blockManagementAccess'] : true), 384 | "lan" => [ 385 | "type" => (isset($config['lan']['type']) ? $config['lan']['type'] : "bridge"), 386 | "interfaceNames" => (isset($config['lan']['interfaceNames']) ? $config['lan']['interfaceNames'] : ['lan0','wan0']), 387 | "gateway" => (isset($config['lan']['gateway']) ? $config['lan']['gateway'] : null), 388 | "cidr" => (isset($config['lan']['cidr']) ? $config['lan']['cidr'] : "192.168.1.1/24"), 389 | "proto" => (isset($config['lan']['proto']) ? $config['lan']['proto'] : 'dhcp'), 390 | "dns" => (isset($config['lan']['dns']) ? $config['lan']['dns'] : [null,null]), 391 | "dhcp" => [ 392 | "ignore" => (isset($config['lan']['dhcp']['ignore']) ? $config['lan']['dhcp']['ignore'] : true), 393 | "interface" => (isset($config['lan']['dhcp']['interface']) ? $config['lan']['dhcp']['interface'] : "lan"), 394 | "rangeStart" => (isset($config['lan']['dhcp']['rangeStart']) ? $config['lan']['dhcp']['rangeStart'] : "192.168.1.100"), 395 | "rangeEnd" => (isset($config['lan']['dhcp']['rangeEnd']) ? $config['lan']['dhcp']['rangeEnd'] : '192.168.1.250'), 396 | "leaseTime" => (isset($config['lan']['dhcp']['leaseTime']) ? $config['lan']['dhcp']['leaseTime'] : '12h'), 397 | ], 398 | ], 399 | "wan" => [ 400 | "enabled" => (isset($config['wan']['enabled']) ? $config['wan']['enabled'] : false), 401 | "interfaceNames" => [null], 402 | "cidr" => null, 403 | 404 | "gateway" => null, 405 | "proto" => 'dhcp', 406 | "dns" => [null, null], 407 | "service" => null, 408 | "username" => null, 409 | "password" => null, 410 | ], 411 | "mgt" => [ 412 | "enabled" => true, 413 | "vlanId" => 103, 414 | "proto" => "dhcp", 415 | "cidr" => null, 416 | "service" => null, 417 | "username" => null, 418 | "password" => null 419 | ], 420 | ]; 421 | 422 | $config = $defaultConfig; 423 | 424 | $json = json_encode($config); 425 | 426 | $this->request_type = 'PUT'; 427 | 428 | $response = $this->exec_curl('/v2.1/devices/aircubes/'.$aircubeId.'/config/network', $json); 429 | return $this->process_response($response); 430 | 431 | } 432 | 433 | /** 434 | * Set the aircube wireless configuration 435 | * @param string $aircubeId the aircube ID to set the config 436 | * @param array $config the configuration array (see UNMS doc for details. Upcoming here in future version) 437 | * 438 | * @return object The return value from the API 439 | */ 440 | public function setAircubeWirelessConfig($aircubeId, $config) 441 | { 442 | if (!$this->is_loggedin) return false; 443 | $defaultConfig = [ 444 | "wifi2Ghz" => [ 445 | "enabled" => true, 446 | "available" => true, 447 | "mode" => "ap", 448 | "ssid" => "#MonSansFil", 449 | "country" => "CA", 450 | "channel" => "auto", 451 | "channelWidth" => 20, 452 | "encryption" => "wpa2", 453 | "authentication" => "psk2", 454 | "txPower" => 12, 455 | "key" => "monsansfilaircube", 456 | 'isWPA2PSKEnabled' => true, 457 | ], 458 | "wifi5Ghz" => [ 459 | "enabled" => false, 460 | "available" => true, 461 | "mode" => "ap", 462 | "ssid" => "#MonSansFil", 463 | "country" => "CA", 464 | "channel" => "auto", 465 | "channelWidth" => 80, 466 | "encryption" => "wpa2", 467 | "authentication" => "psk2", 468 | "txPower" => 22, 469 | "key" => "monsansfilaircube", 470 | 'isWPA2PSKEnabled' => true, 471 | ], 472 | ]; 473 | 474 | $config = array_merge($defaultConfig,array_intersect_key($config, $defaultConfig)); 475 | $config['wifi2Ghz'] = array_merge($defaultConfig['wifi2Ghz'],array_intersect_key($config['wifi2Ghz'], $defaultConfig['wifi2Ghz'])); 476 | $config['wifi5Ghz'] = array_merge($defaultConfig['wifi5Ghz'],array_intersect_key($config['wifi5Ghz'], $defaultConfig['wifi5Ghz'])); 477 | 478 | $json = json_encode($config); 479 | 480 | $this->request_type = 'PUT'; 481 | 482 | $response = $this->exec_curl('/v2.1/devices/aircubes/'.$aircubeId.'/config/wireless', $json); 483 | return $this->process_response($response); 484 | 485 | } 486 | 487 | 488 | 489 | /** 490 | * Get the devices at the root of a site. This will not return child sites devices 491 | * @param string $siteId the site Id to get the devices from 492 | * @return object The return value from the API or FALSE if an error occured 493 | */ 494 | public function getDevices($siteId) 495 | { 496 | if (!$this->is_loggedin) return false; 497 | $response = $this->exec_curl('/v2.1/devices?siteId='.$siteId); 498 | return $this->process_response($response); 499 | } 500 | 501 | /** 502 | * Get the interfaces from the router specified 503 | * @param string $deviceId The router's device ID 504 | * @return object The return value from the API or FALSE if an error occured 505 | */ 506 | public function getRouterInterfaces($deviceId) 507 | { 508 | if (!$this->is_loggedin) return false; 509 | $response = $this->exec_curl('/v2.1/devices/'.$deviceId.'/interfaces'); 510 | return $this->process_response($response); 511 | } 512 | 513 | /** 514 | * Get the specified aircube's data 515 | * @param string $aircubeId the aircube Id to get the data from 516 | * @return object The return value from the API or FALSE if an error occured 517 | */ 518 | public function getAirCubeData($aircubeId) 519 | { 520 | if (!$this->is_loggedin) return false; 521 | $response = $this->exec_curl('/v2.1/devices/aircubes/'.$aircubeId); 522 | return $this->process_response($response); 523 | } 524 | 525 | /** 526 | * Get the clients connected to the specified aircube 527 | * @param string $aircubeId the aircube Id to get the clients from 528 | * @return object The return value from the API or FALSE if an error occured 529 | */ 530 | public function getAirCubeDevices($aircubeId) 531 | { 532 | if (!$this->is_loggedin) return false; 533 | $response = $this->exec_curl('/v2.1/devices/aircubes/'.$aircubeId.'/stations'); 534 | return $this->process_response($response); 535 | } 536 | 537 | /** 538 | * Get the DHCP Lease from the specified router 539 | * @param string $id the router device Id to get the DHCP Lease from 540 | * @return object The return value from the API or FALSE if an error occured 541 | */ 542 | public function geteRouterDHCPLeases($id) 543 | { 544 | if (!$this->is_loggedin) return false; 545 | $response = $this->exec_curl('/v2.1/devices/erouters/'.$id.'/dhcp/leases'); 546 | return $this->process_response($response); 547 | } 548 | 549 | /** 550 | * Get the Wireless network informations from an aircube 551 | * @param string $aircubeId the aircube device Id to get the wireless info from 552 | * @return object The return value from the API or FALSE if an error occured 553 | */ 554 | public function getAircubeWirelessInfo($aircubeId) 555 | { 556 | if (!$this->is_loggedin) return false; 557 | $response = $this->exec_curl('/v2.1/devices/aircubes/'.$aircubeId.'/config/wireless'); 558 | return $this->process_response($response); 559 | } 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | /** 573 | * Process regular responses where output is the content of the data array 574 | */ 575 | protected function process_response($response_json) 576 | { 577 | $response = json_decode($response_json); 578 | $this->catch_json_last_error(); 579 | $this->last_results_raw = $response; 580 | 581 | if(is_object($response)) 582 | { 583 | if(isset($response->statusCode) && $response->statusCode == '400') 584 | { 585 | if (isset($response->message)) $this->last_error_message = $response->message; 586 | if ($this->debug) trigger_error('Debug: Last error message: '.$this->last_error_message); 587 | return false; 588 | } 589 | } 590 | 591 | if (is_array($response)) return $response; 592 | if (is_object($response)) return $response; 593 | return true; 594 | 595 | } 596 | 597 | 598 | /** 599 | * Capture the latest JSON error when $this->debug is true 600 | */ 601 | private function catch_json_last_error() 602 | { 603 | if ($this->debug) { 604 | switch (json_last_error()) { 605 | case JSON_ERROR_NONE: 606 | // JSON is valid, no error has occurred 607 | $error = ''; 608 | break; 609 | case JSON_ERROR_DEPTH: 610 | $error = 'The maximum stack depth has been exceeded'; 611 | break; 612 | case JSON_ERROR_STATE_MISMATCH: 613 | $error = 'Invalid or malformed JSON.'; 614 | break; 615 | case JSON_ERROR_CTRL_CHAR: 616 | $error = 'Control character error, possibly incorrectly encoded'; 617 | break; 618 | case JSON_ERROR_SYNTAX: 619 | $error = 'Syntax error, malformed JSON.'; 620 | break; 621 | case JSON_ERROR_UTF8: 622 | // PHP >= 5.3.3 623 | $error = 'Malformed UTF-8 characters, possibly incorrectly encoded'; 624 | break; 625 | case JSON_ERROR_RECURSION: 626 | // PHP >= 5.5.0 627 | $error = 'One or more recursive references in the value to be encoded'; 628 | break; 629 | case JSON_ERROR_INF_OR_NAN: 630 | // PHP >= 5.5.0 631 | $error = 'One or more NAN or INF values in the value to be encoded'; 632 | break; 633 | case JSON_ERROR_UNSUPPORTED_TYPE: 634 | $error = 'A value of a type that cannot be encoded was given'; 635 | break; 636 | case JSON_ERROR_INVALID_PROPERTY_NAME: 637 | // PHP >= 7.0.0 638 | $error = 'A property name that cannot be encoded was given'; 639 | break; 640 | case JSON_ERROR_UTF16: 641 | // PHP >= 7.0.0 642 | $error = 'Malformed UTF-16 characters, possibly incorrectly encoded'; 643 | break; 644 | default: 645 | // we have an unknown error 646 | $error = 'Unknown JSON error occured.'; 647 | break; 648 | } 649 | 650 | if ($error !== '') { 651 | trigger_error('JSON decode error: ' . $error); 652 | return false; 653 | } 654 | } 655 | 656 | return true; 657 | } 658 | 659 | /** 660 | * Check the submitted base URL 661 | */ 662 | private function check_base_url() 663 | { 664 | $url_valid = filter_var('http://'.$this->baseurl, FILTER_VALIDATE_URL); 665 | if (!$url_valid) 666 | { 667 | trigger_error('The URL provided is incomplete or invalid!'); 668 | return false; 669 | } 670 | 671 | $base_url_components = parse_url('http://'.$this->baseurl); 672 | if (empty($base_url_components['port'])) 673 | { 674 | trigger_error('The URL provided does not have a port suffix, normally this is :8444'); 675 | return false; 676 | } 677 | 678 | return true; 679 | } 680 | 681 | /** 682 | * Logout from UNMS Controller 683 | * ---------------------------- 684 | * returns true upon success 685 | */ 686 | public function logout() 687 | { 688 | if (!$this->is_loggedin) return false; 689 | $this->exec_curl('/logout'); 690 | $this->is_loggedin = false; 691 | return true; 692 | } 693 | 694 | /** 695 | * Login to Unms 696 | * ------------------------- 697 | * returns true upon success 698 | */ 699 | public function login() 700 | { 701 | /** 702 | * if user has token set, skip the login 703 | */ 704 | if (isset($this->token) && $this->token != '') return $this->is_loggedin = true; 705 | 706 | 707 | $logindata = array('password' => $this->password, 'username' => $this->user, 'sessionTimeout' => '3600000'); 708 | $login_json = json_encode($logindata); 709 | 710 | $url = "https://".$this->baseurl."/v2.1/user/login"; 711 | 712 | $headers = array(); 713 | $headers[] = 'Content-Type: application/json'; 714 | 715 | # 716 | #Login to get x-auth-token 717 | # 718 | 719 | $curl_login = curl_init(); 720 | curl_setopt($curl_login, CURLOPT_URL,$url); 721 | curl_setopt($curl_login, CURLOPT_CUSTOMREQUEST, "POST"); 722 | curl_setopt($curl_login, CURLOPT_POSTFIELDS, $login_json); 723 | curl_setopt($curl_login, CURLOPT_HEADER, 1); 724 | curl_setopt($curl_login, CURLOPT_HTTPHEADER, $headers); 725 | curl_setopt($curl_login, CURLOPT_RETURNTRANSFER, true); 726 | 727 | $content = curl_exec($curl_login); 728 | 729 | if (curl_errno($curl_login)) trigger_error('cURL error: '.curl_error($curl_login)); 730 | 731 | if ($this->debug) { 732 | curl_setopt($curl_login, CURLOPT_VERBOSE, true); 733 | 734 | print '
';
735 |                 print PHP_EOL.'-----------LOGIN-------------'.PHP_EOL;
736 |                 print_r (curl_getinfo($curl_login));
737 |                 print PHP_EOL.'----------RESPONSE-----------'.PHP_EOL;
738 |                 print $content;
739 |                 print PHP_EOL.'-----------------------------'.PHP_EOL;
740 |                 print '
'; 741 | } 742 | 743 | 744 | if($content === false) 745 | { 746 | output('Curl error: ' . curl_error($curl_login)); 747 | } 748 | 749 | $header = \Ubnt::get_headers_from_curl_response($content); 750 | 751 | $header_size = curl_getinfo($curl_login, CURLINFO_HEADER_SIZE); 752 | $body = trim(substr($content, $header_size)); 753 | $code = curl_getinfo($curl_login, CURLINFO_HTTP_CODE); 754 | 755 | curl_close ($curl_login); 756 | 757 | $token= false; 758 | 759 | if($header && isset($header['x-auth-token'])) 760 | { 761 | $token = $header['x-auth-token']; 762 | } 763 | 764 | $this->token = $token; 765 | 766 | $this->is_loggedin = true; 767 | return true; 768 | } 769 | 770 | /** 771 | * Execute the cURL request 772 | */ 773 | protected function exec_curl($path, $data = '') 774 | { 775 | $url = 'https://'.$this->baseurl.$path; 776 | 777 | $ch = $this->get_curl_obj(); 778 | curl_setopt($ch, CURLOPT_URL, $url); 779 | 780 | if (trim($data) != ''){ 781 | curl_setopt($ch, CURLOPT_POSTFIELDS, $data); 782 | if ($this->request_type === 'PUT'){ 783 | $headers = array(); 784 | $headers[] = "Accept: application/json"; 785 | $headers[] = "Content-Type: application/json"; 786 | $headers[] = "X-Auth-Token: ".$this->token; 787 | $headers[] = "Content-Length: ".strlen($data); 788 | curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); 789 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); 790 | }else{ 791 | $headers = array(); 792 | $headers[] = "Accept: application/json"; 793 | $headers[] = "Content-Type: application/json"; 794 | $headers[] = "X-Auth-Token: ".$this->token; 795 | curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); 796 | } 797 | }else{ 798 | curl_setopt($ch, CURLOPT_POST, false); 799 | if ($this->request_type === 'DELETE') curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE'); 800 | } 801 | 802 | /** 803 | * execute the cURL request 804 | */ 805 | $content = curl_exec($ch); 806 | if (curl_errno($ch)) { 807 | trigger_error('cURL error: '.curl_error($ch)); 808 | } 809 | 810 | /** 811 | * has the session timed out? 812 | */ 813 | $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 814 | $json_decoded_content = json_decode($content, true); 815 | 816 | if ($http_code == 401 && isset($json_decoded_content['meta']['msg']) && $json_decoded_content['meta']['msg'] === 'api.err.LoginRequired') { 817 | if ($this->debug) error_log('cURL debug: Needed to reconnect to UniFi Controller'); 818 | 819 | /** 820 | * explicitly unset the old token now 821 | */ 822 | if (isset($this->token)) { 823 | $this->token = ''; 824 | } 825 | 826 | $this->login(); 827 | 828 | /** 829 | * when login was okay, exec the same command again 830 | */ 831 | if ($this->is_loggedin) { 832 | curl_close($ch); 833 | 834 | return $this->exec_curl($path, $data); 835 | } 836 | } 837 | 838 | if ($this->debug) { 839 | print '
';
840 |             print PHP_EOL.'---------cURL INFO-----------'.PHP_EOL;
841 |             print_r (curl_getinfo($ch));
842 |             print PHP_EOL.'-------URL & PAYLOAD---------'.PHP_EOL;
843 |             print $url.PHP_EOL;
844 |             print $data;
845 |             print PHP_EOL.'----------RESPONSE-----------'.PHP_EOL;
846 |             print $content;
847 |             print PHP_EOL.'-----------------------------'.PHP_EOL;
848 |             print '
'; 849 | } 850 | 851 | curl_close($ch); 852 | 853 | /** 854 | * set request_type value back to default, just in case 855 | */ 856 | $this->request_type = 'POST'; 857 | 858 | return $content; 859 | } 860 | 861 | /** 862 | * Get the cURL object 863 | */ 864 | private function get_curl_obj() 865 | { 866 | $ch = curl_init(); 867 | 868 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 869 | 870 | $headers = array(); 871 | $headers[] = "Accept: application/json"; 872 | $headers[] = "X-Auth-Token: ".$this->token; 873 | curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); 874 | 875 | if($this->curl_ssl_verify_peer) { 876 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); 877 | } 878 | 879 | if($this->curl_ssl_verify_host) { 880 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, true); 881 | } 882 | 883 | if ($this->debug) curl_setopt($ch, CURLOPT_VERBOSE, true); 884 | 885 | return $ch; 886 | } 887 | } 888 | --------------------------------------------------------------------------------