├── LICENSE ├── README.md ├── composer.json └── src └── Wemo ├── Models ├── Device.php └── Outlet.php └── wsdl └── BasicService.wsdl /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WeMo-PHP-Toolkit 2 | ================ 3 | 4 | * Author: Thorne Melcher (GitHub: ExistentialEnso) 5 | * License: LGPL v3 (more permissive commercial licensing available for a fee on request) 6 | 7 | PHP classes for use with Belkin's WeMo system. Currently only has an "Outlet" class (sorry, that's the only WeMo product 8 | I own!) 9 | 10 | ## Installation 11 | 12 | Composer is the easiest way to manage dependencies in your project. Create a file named 13 | composer.json with the following: 14 | 15 | ```json 16 | { 17 | "require": { 18 | "wemo-php-toolkit/wemo-php-toolkit": "dev-master" 19 | } 20 | } 21 | ``` 22 | 23 | And run Composer to install wemo-php-toolkit: 24 | 25 | ```bash 26 | $ curl -s http://getcomposer.org/installer | php 27 | $ composer.phar install 28 | ``` 29 | 30 | ## Usage 31 | 32 | ```php 33 | $outlet = new Outlet("192.168.1.x"); // Change to location of Outlet on your network 34 | $outlet = new Outlet("192.168.1.x","49513"); // Optional you can add the port where the device is reachable 35 | $outlet->setIsOn(false); // Outlet will shut off! 36 | ``` 37 | 38 | The outlets even save their name and icon to them (which can be changed in the official apps), which you can view: 39 | 40 | ```php 41 | $outlet->getIconUrl(); // e.g. "http://192.168.1.x:49153/icon.png" 42 | $outlet->getDisplayName(); // e.g. "Air Purifier" 43 | $outlet->getManufacturer(); // e.g. "Belkin" 44 | $outlet->getModelDescription(); // e.g. "Belkin Plugin Socket 1.0" 45 | ``` 46 | 47 | 48 | # Version History 49 | 50 | *Version: 0.2.1 (2017-09-26)* 51 | - Optional port can be configured 52 | - If not port is configured automatic search of applicable device port (49152, 49153, 49154) 53 | - Add method refresh on object construct to fill device information properties 54 | 55 | *Version: 0.1.1* 56 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wemo-php-toolkit/wemo-php-toolkit", 3 | "description": "PHP classes for use with Belkin's WeMo system", 4 | "keywords": ["library", "wemo", "belkin"], 5 | "homepage": "https://github.com/ExistentialEnso/WeMo-PHP-Toolkit", 6 | "license": "LGPL v3", 7 | "authors": [ 8 | { 9 | "name": "Thorne Melcher" 10 | } 11 | ], 12 | "support": { 13 | "issues": "https://github.com/ExistentialEnso/WeMo-PHP-Toolkit/issues", 14 | "source": "https://github.com/ExistentialEnso/WeMo-PHP-Toolkit" 15 | }, 16 | "autoload": { 17 | "psr-0": { "Wemo": "src/" } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Wemo/Models/Device.php: -------------------------------------------------------------------------------- 1 | 4 | * @package WeMo-PHP-Toolkit 5 | * @version 0.1 6 | * @license LGPL 7 | */ 8 | 9 | namespace Wemo\Models; 10 | 11 | /** 12 | * Generic WeMo device class. All devices will have these fields. 13 | * 14 | * @package Wemo\Models 15 | */ 16 | abstract class Device { 17 | 18 | /** 19 | * The IP Address of the device. 20 | * 21 | * @var string 22 | */ 23 | protected $ip_address; 24 | 25 | /** 26 | * Device's SOAP port. Defaults to 49153. 27 | * 28 | * @var int 29 | */ 30 | protected $port = 49154; 31 | 32 | /** 33 | * The MAC address of the device. 34 | * 35 | * @var string 36 | */ 37 | protected $mac_address; 38 | 39 | /** 40 | * The display name stored on the device. 41 | * 42 | * @var string 43 | */ 44 | protected $display_name = ""; 45 | 46 | /** 47 | * The manufacturer of the device. 48 | * 49 | * @var string 50 | */ 51 | protected $manufacturer; 52 | 53 | /** 54 | * The manufacturer's URL. 55 | * 56 | * @var string 57 | */ 58 | protected $manufacturer_url; 59 | 60 | /** 61 | * The model of device. 62 | * 63 | * @var string 64 | */ 65 | protected $model_name; 66 | 67 | /** 68 | * A short description of this device. 69 | * 70 | * @var string 71 | */ 72 | protected $model_description; 73 | 74 | /** 75 | * The model number of this device. 76 | * 77 | * @var string 78 | */ 79 | protected $model_number; 80 | 81 | /** 82 | * A URL for this specific model. 83 | * 84 | * @var string 85 | */ 86 | protected $model_url; 87 | 88 | /** 89 | * This device's serial number. 90 | * 91 | * @var string 92 | */ 93 | protected $serial_number; 94 | 95 | /** 96 | * If the device's properties have been fetched yet. $this->refresh() will be called 97 | * from all getters if this is false. Subclasses should implement $this->refresh and 98 | * set this variable appropriately for success or failure. 99 | * 100 | * @var boolean 101 | */ 102 | protected $properties_fetched = false; 103 | 104 | /** 105 | * Updates the devices's state by pulling info from the device itself. 106 | * Subclasses implementing this should set $this->properties_fetched 107 | * appropriately for success or failure. 108 | */ 109 | abstract public function refresh(); 110 | 111 | /** 112 | * @return string 113 | */ 114 | public function getIpAddress() { 115 | return $this->ip_address; 116 | } 117 | 118 | /** 119 | * Gets the SOAP port of the device. 120 | * 121 | * @return int 122 | */ 123 | public function getPort() { 124 | return $this->port; 125 | } 126 | 127 | /** 128 | * Sets the SOAP port of the device. 129 | * 130 | * @param $port 131 | */ 132 | public function setPort($port) { 133 | $this->port = $port; 134 | } 135 | 136 | /** 137 | * @return string 138 | */ 139 | public function getMacAddress() { 140 | if (!$this->properties_fetched) { 141 | $this->refresh(); 142 | } 143 | return $this->mac_address; 144 | } 145 | 146 | /** 147 | * @return string 148 | */ 149 | public function getDisplayName() { 150 | if (!$this->properties_fetched) { 151 | $this->refresh(); 152 | } 153 | return $this->display_name; 154 | } 155 | 156 | /** 157 | * @return string 158 | */ 159 | public function getManufacturer() { 160 | if (!$this->properties_fetched) { 161 | $this->refresh(); 162 | } 163 | return $this->manufacturer; 164 | } 165 | 166 | /** 167 | * @return string 168 | */ 169 | public function getManufacturerUrl() { 170 | if (!$this->properties_fetched) { 171 | $this->refresh(); 172 | } 173 | return $this->manufacturer_url; 174 | } 175 | 176 | /** 177 | * @return string 178 | */ 179 | public function getModelName() { 180 | if (!$this->properties_fetched) { 181 | $this->refresh(); 182 | } 183 | return $this->model_name; 184 | } 185 | 186 | /** 187 | * @return string 188 | */ 189 | public function getModelDescription() { 190 | if (!$this->properties_fetched) { 191 | $this->refresh(); 192 | } 193 | return $this->model_description; 194 | } 195 | 196 | /** 197 | * @return string 198 | */ 199 | public function getModelNumber() { 200 | if (!$this->properties_fetched) { 201 | $this->refresh(); 202 | } 203 | return $this->model_number; 204 | } 205 | 206 | /** 207 | * @return string 208 | */ 209 | public function getModelUrl() { 210 | if (!$this->properties_fetched) { 211 | $this->refresh(); 212 | } 213 | return $this->model_url; 214 | } 215 | 216 | /** 217 | * @return string 218 | */ 219 | public function getSerialNumber() { 220 | if (!$this->properties_fetched) { 221 | $this->refresh(); 222 | } 223 | return $this->serial_number; 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/Wemo/Models/Outlet.php: -------------------------------------------------------------------------------- 1 | 4 | * @package WeMo-PHP-Toolkit 5 | * @version 0.1 6 | * @license LGPL 7 | */ 8 | 9 | namespace Wemo\Models; 10 | 11 | /** 12 | * Model class representing a WeMo Outlet. Connects over IP, so it must be accessible to the server running the PHP 13 | * app at the IP specified. 14 | * 15 | * @package Wemo\Models 16 | */ 17 | class Outlet extends Device { 18 | 19 | /** 20 | * The URL of the icon to display for this outlet. 21 | * 22 | * @var string 23 | */ 24 | protected $icon_url; 25 | 26 | /** 27 | * Constructor method. Will populate other information beyond IP address from the outlet itself. 28 | * 29 | * @param $ip_address 30 | */ 31 | public function __construct($ip_address, $port = null) { 32 | $this->ip_address = $ip_address; 33 | if ( $port ) $this->port = $port; 34 | $this->refresh(); 35 | } 36 | 37 | /** 38 | * Updates the Outlet's state by pulling info from the outlet itself. 39 | */ 40 | public function refresh() { 41 | // Squelching is bad practice, but we're handling failures 42 | $contents = @file_get_contents("http://" . $this->ip_address . ":" . $this->port . "/setup.xml"); 43 | 44 | if($contents === false) { 45 | 46 | if ($this->port == "49154") { 47 | $this->port = "49153"; 48 | $this->refresh(); 49 | return; 50 | } 51 | elseif ( $this->port == "49153") { 52 | $this->port = "49152"; 53 | $this->refresh(); 54 | return; 55 | } 56 | 57 | $this->properties_fetched; 58 | trigger_error("Unable to connect to outlet at " . $this->ip_address, E_USER_WARNING); 59 | } 60 | 61 | $contents = new \SimpleXMLElement($contents); 62 | 63 | $this->manufacturer = (string) $contents->device->manufacturer; 64 | $this->manufacturer_url = (string) $contents->device->manufacturerURL; 65 | $this->model_description = (string) $contents->device->modelDescription; 66 | $this->model_name = (string) $contents->device->modelName; 67 | $this->model_number = (string) $contents->device->modelNumber; 68 | $this->model_url = (string) $contents->device->modelURL; 69 | $this->serial_number = (string) $contents->device->serialNumber; 70 | $this->display_name = (string) $contents->device->friendlyName; 71 | $this->mac_address = (string) $contents->device->macAddress; 72 | $this->icon_url = "http://" . $this->ip_address . ":" . $this->port . "/" . $contents->device->iconList->icon->url; 73 | 74 | $this->properties_fetched = true; 75 | } 76 | 77 | /** 78 | * @return string 79 | */ 80 | public function getIconUrl() { 81 | return $this->icon_url; 82 | } 83 | 84 | /** 85 | * Whether or not this outlet is on. 86 | * 87 | * @return boolean 88 | */ 89 | public function getIsOn() { 90 | $location = 'http://'.$this->ip_address.':' . $this->port . '/upnp/control/basicevent1'; 91 | $action = 'urn:Belkin:service:basicevent:1#GetBinaryState'; 92 | 93 | $client = new \SoapClient(dirname(__DIR__) . "/wsdl/BasicService.wsdl"); 94 | $xml = ''; 95 | 96 | try { 97 | $response = $client->__doRequest($xml, $location, $action, 1, false); 98 | preg_match("/(\d)<\/BinaryState>/", $response, $matches); 99 | 100 | return ($matches[1] == 1); 101 | } catch (SoapFault $exception) { 102 | // Our soap ain't faulty, but PHP doesn't want us to drop the soap and will generate low-level warnings 103 | } 104 | } 105 | 106 | /** 107 | * Sets whether or not this outlet is on. Makes the SOAP call to the outlet itself to enact the change. 108 | * 109 | * @param bool $is_on 110 | * @return boolean 111 | */ 112 | public function setIsOn($is_on) { 113 | if ($this->getIsOn() == $is_on) { 114 | return $is_on; 115 | } 116 | 117 | $on_off = $is_on ? "1" : "0"; 118 | $location = 'http://'.$this->ip_address.':' . $this->port . '/upnp/control/basicevent1'; 119 | $action = 'urn:Belkin:service:basicevent:1#SetBinaryState'; 120 | 121 | $client = new \SoapClient(dirname(__DIR__) . "/wsdl/BasicService.wsdl"); 122 | $xml = ''.$on_off.''; 123 | 124 | try { 125 | $response = $client->__doRequest($xml, $location, $action, 1, false); 126 | preg_match("/(\d)<\/BinaryState>/", $response, $matches); 127 | 128 | return ($matches[1] == 1); 129 | } catch (SoapFault $exception) { 130 | // Our soap ain't faulty, but PHP doesn't want us to drop the soap and will generate low-level warnings 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/Wemo/wsdl/BasicService.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | --------------------------------------------------------------------------------