├── 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 |
--------------------------------------------------------------------------------