├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── build └── README.md ├── composer.json ├── composer └── bin │ └── roku ├── phpunit.dist.xml ├── src └── Roku │ ├── Application.php │ ├── Commands │ ├── Acceleration.php │ ├── Command.php │ ├── Magnetic.php │ ├── Orientation.php │ ├── Rotation.php │ ├── Sensor.php │ └── Touch.php │ ├── Console │ └── Console.php │ ├── Device.php │ ├── Exception.php │ ├── Roku.php │ └── Utils │ ├── AbstractEnum.php │ ├── Http.php │ └── HttpConsole.php └── tests ├── Roku ├── Commands │ ├── CommandTest.php │ └── SensorTest.php └── RokuTest.php ├── bootstrap.php └── data ├── apps.xml ├── roku.xml └── samples.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # cache directories 2 | Thumbs.db 3 | *.DS_Store 4 | *.empty 5 | 6 | # phpstorm project files 7 | .idea 8 | 9 | # eclipse 10 | .buildpath 11 | .project 12 | .settings 13 | 14 | # SublimeText 15 | /*.sublime-project 16 | 17 | .htaccess 18 | phpunit.xml 19 | vendor 20 | composer.phar 21 | 22 | #composer 23 | /composer.lock 24 | 25 | # Windows image file caches 26 | Thumbs.db 27 | 28 | # Folder config file 29 | Desktop.ini 30 | 31 | *~ 32 | \#*\# 33 | /.emacs.desktop 34 | /.emacs.desktop.lock 35 | .elc 36 | auto-save-list 37 | tramp 38 | .\#* 39 | 40 | .DS_Store 41 | 42 | # Thumbnails 43 | ._* 44 | 45 | # Files that might appear on external disk 46 | .Spotlight-V100 47 | .Trashes 48 | 49 | # Build 50 | /build/logs 51 | /build/pdepend 52 | 53 | #Sublime 54 | *.sublime-project 55 | *.sublime-workspace 56 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.1.18 5 | 6 | 7 | before_script: 8 | - composer install --no-interaction --prefer-source 9 | - cp phpunit.dist.xml phpunit.xml 10 | 11 | script: 12 | - mkdir -p build/logs 13 | - ./vendor/bin/phpunit 14 | 15 | # after_script: 16 | # - php vendor/bin/coveralls -v -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Roku PHP Library 2 | ================================================ 3 | 4 | [![Build Status](https://api.travis-ci.org/svilborg/php-roku.png?branch=master)](https://travis-ci.org/svilborg/php-roku) 5 | [![Latest Stable Version](https://poser.pugx.org/svilborg/php-roku/v/stable.png)](https://packagist.org/packages/svilborg/php-roku) 6 | [![Latest Unstable Version](https://poser.pugx.org/svilborg/php-roku/v/unstable.png)](https://packagist.org/packages/svilborg/php-roku) 7 | 8 | 9 | PHP Library for communication with Roku External Control Protocol 10 | 11 | Installation 12 | ================ 13 | 14 | Installing via Composer 15 | ----------------------- 16 | Install composer in a common location or in your project: 17 | 18 | ```bash 19 | curl -s http://getcomposer.org/installer | php 20 | ``` 21 | 22 | Create the composer.json file as follows: 23 | 24 | ```json 25 | { 26 | "require": { 27 | "svilborg/php-roku": "dev-master" 28 | } 29 | } 30 | ``` 31 | 32 | Run the composer installer: 33 | 34 | ```bash 35 | php composer.phar install 36 | ``` 37 | 38 | Requirements 39 | ============ 40 | 41 | * PHP Version >=5.3.2. 42 | * PHP Httpful Library 43 | 44 | Usage 45 | ===== 46 | 47 | Execute commands : 48 | 49 | ```php 50 | 51 | $roku = new \Roku\Roku("192.168.72.10", 8060, 0.2); 52 | 53 | $roku->up(); 54 | 55 | $roku->select(); 56 | 57 | $roku->literals("test@gmail.com"); 58 | 59 | $roku->down(); 60 | 61 | $roku->down(); 62 | 63 | $roku->select(); 64 | 65 | ``` 66 | 67 | List the applicatioin installed on the device : 68 | 69 | ```php 70 | 71 | 72 | $roku = new \Roku\Roku("192.168.72.10", 8060, 0.2); 73 | 74 | $applications = $roku->apps(); 75 | 76 | foreach ($applications as $application) { 77 | echo $application->getId(); 78 | echo $application->getVersion(); 79 | echo $application->getName(); 80 | echo "\n"; 81 | } 82 | 83 | ``` 84 | 85 | Get device information : 86 | 87 | ```php 88 | 89 | 90 | $roku = new \Roku\Roku("192.168.72.10", 8060, 0.2); 91 | 92 | $device = $roku->device(); 93 | 94 | echo $device->getSerialNumber(); 95 | echo $device->getModelName(); 96 | echo $device->getModelDescription(); 97 | // etc.. 98 | 99 | 100 | ``` 101 | 102 | Usage Commandline 103 | ================= 104 | 105 | For the list of commands execute : 106 | 107 | ```bash 108 | 109 | $ vendor/bin/roku --help 110 | 111 | ``` 112 | 113 | It displays : 114 | 115 | ```bash 116 | 117 | PHP Roku Console 118 | 119 | Usage: roku [OPTION] .. 120 | 121 | -h Host 122 | -p Port 123 | -d Delay between each command 124 | -i Interactive mode (Listens for keyboard keystrokes) 125 | -c Command mode (Specify commands to be executed, Example -c "up down test@gmail.com down select home") 126 | -t Test Mode (Does not send commands.Just simulates them.) 127 | --help Shows this help 128 | 129 | ``` 130 | 131 | Example usage of command and interactive modes : 132 | 133 | ```bash 134 | 135 | $ vendor/bin/roku -h 192.168.72.10 -p 8060 -d 1 -c "up test@gmailc.om down down select home" 136 | 137 | $ vendor/bin/roku -h 192.168.72.10 -d 1 -i 138 | 139 | ``` 140 | 141 | Running the tests 142 | ================= 143 | 144 | First, install PHPUnit with `composer.phar install --dev`, then run 145 | `./vendor/bin/phpunit`. 146 | -------------------------------------------------------------------------------- /build/README.md: -------------------------------------------------------------------------------- 1 | Build Folder -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"svilborg/php-roku", 3 | "type":"library", 4 | "description":"PHP Library for communication with Roku External Control Protocol", 5 | "keywords":[ 6 | "Roku", "http" 7 | ], 8 | "license": "Apache-2.0", 9 | "authors":[ 10 | { 11 | "name":"Svilborg", 12 | "email":"svilborg@gmail.com" 13 | } 14 | ], 15 | "support": { 16 | "issues": "https://github.com/svilborg/php-roku?state=open", 17 | "source": "https://github.com/svilborg/php-roku" 18 | }, 19 | "require":{ 20 | "php":">=7.0.0", 21 | "nategood/httpful": "*" 22 | }, 23 | "require-dev":{ 24 | "phpunit/phpunit": "^4.8.35 || ^5.4.3 || ^6.0", 25 | "satooshi/php-coveralls": "2.*" 26 | }, 27 | "autoload":{ 28 | "psr-0":{ 29 | "Roku":"src/" 30 | } 31 | }, 32 | "bin": ["composer/bin/roku"] 33 | } -------------------------------------------------------------------------------- /composer/bin/roku: -------------------------------------------------------------------------------- 1 | #! /usr/bin/php 2 | start(); 31 | -------------------------------------------------------------------------------- /phpunit.dist.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | ./tests 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | ./vendor 19 | ./docs 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/Roku/Application.php: -------------------------------------------------------------------------------- 1 | id = $id; 36 | $this->version = $version; 37 | $this->name = $name; 38 | } 39 | 40 | /** 41 | * Get Id 42 | * @return string 43 | */ 44 | public function getId() { 45 | return $this->id; 46 | } 47 | 48 | /** 49 | * Get Version 50 | * @return string 51 | */ 52 | public function getVersion() { 53 | return $this->version; 54 | } 55 | 56 | /** 57 | * Get Name 58 | * @return string 59 | */ 60 | public function getName() { 61 | return $this->name; 62 | } 63 | 64 | /** 65 | * Set Application Id 66 | * 67 | * @param string $id 68 | * @return \Roku\Application 69 | */ 70 | public function setId($id) { 71 | $this->id = $id; 72 | return $this; 73 | } 74 | 75 | /** 76 | * Set Version 77 | * @param string $version 78 | * @return \Roku\Application 79 | */ 80 | public function setVersion($version) { 81 | $this->version = $version; 82 | return $this; 83 | } 84 | 85 | /** 86 | * Set Application Name 87 | * @param string $name 88 | * @return \Roku\Application 89 | */ 90 | public function setName($name) { 91 | $this->name = $name; 92 | return $this; 93 | } 94 | } -------------------------------------------------------------------------------- /src/Roku/Commands/Acceleration.php: -------------------------------------------------------------------------------- 1 | help(); 26 | } 27 | else { 28 | $this->roku = new \Roku\Roku($host, $port, $delay); 29 | 30 | if(isset($options["t"])) { 31 | $this->roku->setClient(new \Roku\Utils\HttpConsole()); 32 | } 33 | 34 | if(isset($options["i"])) { 35 | try { 36 | $this->interactive(); 37 | } 38 | catch(\Exception $e) { 39 | echo "Error " . $e->getMessage(); 40 | echo "\n"; 41 | } 42 | } 43 | elseif(isset($options["c"])) { 44 | try { 45 | $this->commands($options["c"]); 46 | } 47 | catch(\Exception $e) { 48 | echo "Error " . $e->getMessage(); 49 | echo "\n"; 50 | } 51 | } 52 | else { 53 | $this->help(); 54 | } 55 | } 56 | } 57 | 58 | public function commands($commands) { 59 | $commands = explode(" ", $commands); 60 | 61 | foreach($commands as $command) { 62 | if(\Roku\Commands\Command::hasName($command)) { 63 | $this->roku->$command(); 64 | } 65 | else { 66 | $this->roku->literals($command); 67 | } 68 | } 69 | } 70 | 71 | public function interactive() { 72 | system("stty -icanon"); 73 | 74 | while ($c = (fread(STDIN, 4))) { 75 | 76 | $key = $c; 77 | 78 | echo "\n"; 79 | echo ord($c); 80 | 81 | try { 82 | //Special Keys 83 | if(ord($c) == 27) { 84 | 85 | if(strpos($c, '[')) { 86 | 87 | if(strpos($c, 'B')) { 88 | $this->roku->down(); 89 | } 90 | else if(strpos($c, 'A')) { 91 | $this->roku->up(); 92 | } 93 | else if(strpos($c, 'D')) { 94 | $this->roku->left(); 95 | } 96 | else if(strpos($c, 'C')) { 97 | $this->roku->right(); 98 | } 99 | else { 100 | 101 | } 102 | } 103 | 104 | if(strpos($c, 'O')) { 105 | 106 | if(strpos($c, 'H')) { 107 | $this->roku->home(); 108 | } 109 | elseif(strpos($c, 'F')) { 110 | $this->roku->back(); 111 | } 112 | } 113 | 114 | if(strpos($c, '5')) { 115 | $this->roku->fwd(); 116 | } 117 | 118 | if(strpos($c, '6')) { 119 | $this->roku->info(); 120 | } 121 | } 122 | elseif(ord($c) == 10) { 123 | $this->roku->select(); 124 | } 125 | else { 126 | $this->roku->lit($key); 127 | } 128 | } 129 | catch(\Exception $e) { 130 | echo "Not registered keystroke - " . $key; 131 | } 132 | 133 | 134 | } 135 | } 136 | 137 | private function help() { 138 | 139 | echo << Host 145 | -p Port 146 | -d Delay between each command 147 | -i Interactive mode (Listens for keyboard keystrokes) 148 | -c Command mode (Specify commands to be executed, Example -c "up down test@gmail.com down select home") 149 | -t Test Mode (Does not send commands.Just simulates them.) 150 | --help Shows this help 151 | 152 | 153 | EOT; 154 | } 155 | 156 | 157 | } -------------------------------------------------------------------------------- /src/Roku/Device.php: -------------------------------------------------------------------------------- 1 | manufacturerURL; 36 | } 37 | 38 | /** 39 | * Set manufacturerURL 40 | * 41 | * @param string $manufacturerURL 42 | * @return \Roku\Device 43 | */ 44 | public function setManufacturerURL($manufacturerURL) { 45 | $this->manufacturerURL = $manufacturerURL; 46 | return $this; 47 | } 48 | 49 | /** 50 | * Get manufacturerURL 51 | */ 52 | public function getModelDescription() { 53 | return $this->modelDescription; 54 | } 55 | 56 | /** 57 | * Set modelDescription 58 | * 59 | * @param string $modelDescription 60 | * @return \Roku\Device 61 | */ 62 | public function setModelDescription($modelDescription) { 63 | $this->modelDescription = $modelDescription; 64 | return $this; 65 | } 66 | 67 | /** 68 | * Set modelName 69 | */ 70 | public function getModelName() { 71 | return $this->modelName; 72 | } 73 | 74 | /** 75 | * Set modelName 76 | * 77 | * @param string $modelName 78 | * @return \Roku\Device 79 | */ 80 | public function setModelName($modelName) { 81 | $this->modelName = $modelName; 82 | return $this; 83 | } 84 | 85 | /** 86 | * Get modelNumber 87 | */ 88 | public function getModelNumber() { 89 | return $this->modelNumber; 90 | } 91 | 92 | /** 93 | * Set modelNumber 94 | * 95 | * @param string $modelNumber 96 | * @return \Roku\Device 97 | */ 98 | public function setModelNumber($modelNumber) { 99 | $this->modelNumber = $modelNumber; 100 | return $this; 101 | } 102 | 103 | /** 104 | * Get modelURL 105 | */ 106 | public function getModelURL() { 107 | return $this->modelURL; 108 | } 109 | 110 | /** 111 | * Set modelURL 112 | * 113 | * @param string $modelURL 114 | * @return \Roku\Device 115 | */ 116 | public function setModelURL($modelURL) { 117 | $this->modelURL = $modelURL; 118 | return $this; 119 | } 120 | 121 | /** 122 | * Get setModelURL 123 | * 124 | * @return string 125 | */ 126 | public function getSerialNumber() { 127 | return $this->serialNumber; 128 | } 129 | 130 | /** 131 | * Set serialNumber 132 | * 133 | * @param string $serialNumber 134 | * @return \Roku\Device 135 | */ 136 | public function setSerialNumber($serialNumber) { 137 | $this->serialNumber = $serialNumber; 138 | return $this; 139 | } 140 | 141 | /** 142 | * Get UDN 143 | * 144 | * @return string 145 | */ 146 | public function getUDN() { 147 | return $this->udn; 148 | } 149 | 150 | /** 151 | * Set udn 152 | * 153 | * @param string $udn 154 | * @return \Roku\Device 155 | */ 156 | public function setUDN($udn) { 157 | $this->udn = $udn; 158 | return $this; 159 | } 160 | 161 | /** 162 | * Get Device Type 163 | * 164 | * @return string 165 | */ 166 | public function getDeviceType() { 167 | return $this->deviceType; 168 | } 169 | 170 | /** 171 | * Set deviceType 172 | * 173 | * @param string $deviceType 174 | * @return \Roku\Device 175 | */ 176 | public function setDeviceType($deviceType) { 177 | $this->deviceType = $deviceType; 178 | return $this; 179 | } 180 | 181 | public function getFriendlyName() { 182 | return $this->friendlyName; 183 | } 184 | 185 | /** 186 | * Set friendlyName 187 | * 188 | * @param string $friendlyName 189 | * @return \Roku\Device 190 | */ 191 | public function setFriendlyName($friendlyName) { 192 | $this->friendlyName = $friendlyName; 193 | return $this; 194 | } 195 | 196 | /** 197 | * Get Manufacturer 198 | * 199 | * @return string 200 | */ 201 | public function getManufacturer() { 202 | return $this->manufacturer; 203 | } 204 | 205 | /** 206 | * Set Manufacturer 207 | * 208 | * @param string $manufacturer 209 | * @return \Roku\Device 210 | */ 211 | public function setManufacturer($manufacturer) { 212 | $this->manufacturer = $manufacturer; 213 | return $this; 214 | } 215 | 216 | /** 217 | * Get serviceList 218 | * 219 | * @return string 220 | */ 221 | public function getServiceList() { 222 | return $this->serviceList; 223 | } 224 | 225 | /** 226 | * Init from Xml 227 | * 228 | * @param \SimpleXMLElement $xml 229 | * @return \Roku\Device 230 | */ 231 | public function init(\SimpleXMLElement $xml) { 232 | $xml = $this->xml2Array($xml); 233 | 234 | foreach ($xml as $name => $value) { 235 | if ($name == "UDN") { 236 | $name = strtolower($name); 237 | } 238 | 239 | $this->$name = $value; 240 | } 241 | 242 | return $this; 243 | } 244 | 245 | /** 246 | * XML To Array 247 | * 248 | * @param \SimpleXMLElement $xml 249 | * @return array 250 | */ 251 | function xml2Array(\SimpleXMLElement $xml) { 252 | // $xml = simplexml_load_string($string, 'SimpleXMLElement', LIBXML_NOCDATA); 253 | $array = json_decode(json_encode($xml), TRUE); 254 | 255 | return $array; 256 | } 257 | } -------------------------------------------------------------------------------- /src/Roku/Exception.php: -------------------------------------------------------------------------------- 1 | host = $host; 49 | $this->port = $port ? $port : 8060; 50 | $this->delay = $delay; 51 | $this->client = new \Roku\Utils\Http(); 52 | } 53 | 54 | /** 55 | * Set Client Instance 56 | * 57 | * @param \Roku\Utils\Http $client 58 | */ 59 | public function setClient($client) { 60 | $this->client = $client; 61 | } 62 | 63 | /** 64 | * Catchase all function calls 65 | * 66 | * @param string $name Function name 67 | * @param array $fargs Arguments 68 | */ 69 | public function __call($name, $fargs) { 70 | 71 | 72 | if (Command::hasName($name)) { 73 | if (strtoupper(Command::LIT) == strtoupper($name)) { 74 | 75 | $command = Command::LIT . "_" . ($fargs[0] ? urlencode($fargs[0]) : ""); 76 | 77 | return $this->keypress($command); 78 | } 79 | else { 80 | return $this->keypress($name); 81 | } 82 | } 83 | elseif (Sensor::hasName($name)) { 84 | 85 | $params = array(); 86 | $axis = array("x", "y", "z"); 87 | 88 | foreach($fargs as $i => $value) { 89 | $params[strtolower($name) . "." . $axis[$i]] = $value; 90 | } 91 | 92 | return $this->input($params); 93 | } 94 | else { 95 | throw new Exception("Command Not Found"); 96 | } 97 | } 98 | 99 | /** 100 | * Send text as multiple literal calls 101 | * 102 | * @param string $text Text 103 | * @throws Exception 104 | * @return void 105 | */ 106 | public function literals($text) { 107 | $chars = str_split($text); 108 | 109 | foreach ($chars as $char) { 110 | $this->lit($char); 111 | } 112 | } 113 | 114 | /** 115 | * Keypress 116 | * 117 | * @param string $command Command name 118 | * @throws Exception 119 | */ 120 | public function keypress($command) { 121 | $response = $this->client->post($this->getUri("keypress", $command)); 122 | 123 | $this->delay(); 124 | 125 | if ($response->code !== 200) { 126 | throw new Exception("Command Error - " . $command, $response->code); 127 | } 128 | 129 | return $response->raw_body; 130 | } 131 | 132 | /** 133 | * Keydown 134 | * 135 | * @param string $command Command name 136 | * @throws Exception 137 | */ 138 | public function keydown($command) { 139 | $response = $this->client->post($this->getUri("keydown", $command)); 140 | 141 | $this->delay(); 142 | 143 | if ($response->code !== 200) { 144 | throw new Exception("Command Error - " . $command, $response->code); 145 | } 146 | 147 | return $response->raw_body; 148 | } 149 | 150 | /** 151 | * Keyup 152 | * 153 | * @param string $command Command name 154 | * @throws Exception 155 | */ 156 | public function keyup($command) { 157 | $response = $this->client->post($this->getUri("keyup", $command)); 158 | 159 | $this->delay(); 160 | 161 | if ($response->code !== 200) { 162 | throw new Exception("Command Error - " . $command, $response->code); 163 | } 164 | 165 | return $response->raw_body; 166 | } 167 | 168 | /** 169 | * Get Device Information 170 | * 171 | * @throws Exception 172 | * @return \Roku\Device 173 | */ 174 | public function device() { 175 | $response = $this->client->get($this->getUri()); 176 | 177 | if ($response->code !== 200) { 178 | throw new Exception("Command Error - device", $response->code); 179 | } 180 | 181 | $deviceElement = simplexml_load_string($response->body); 182 | 183 | if ($deviceElement === false) { 184 | throw new Exception("Parse Error"); 185 | } 186 | 187 | $device = new Device(); 188 | 189 | $device->init($deviceElement->device); 190 | 191 | return $device; 192 | } 193 | 194 | /** 195 | * Get Applications 196 | * 197 | * @throws Exception 198 | * @return array:\Roku\Application 199 | */ 200 | public function apps() { 201 | $response = $this->client->get($this->getUri("query", "apps")); 202 | 203 | if ($response->code !== 200) { 204 | throw new Exception("Command Error - apps", $response->code); 205 | } 206 | 207 | $apps = simplexml_load_string($response->body); 208 | 209 | if ($apps === false) { 210 | throw new Exception("Parse Error"); 211 | } 212 | 213 | $result = array(); 214 | 215 | foreach ($apps as $app) { 216 | 217 | $id = (string) $app->attributes()->id; 218 | $version = (string) $app->attributes()->version; 219 | $name = (string) $app; 220 | 221 | $result[] = new Application($id, $version, $name); 222 | } 223 | 224 | return $result; 225 | } 226 | 227 | /** 228 | * Get Application Icon 229 | * 230 | * @param Application $app 231 | * @throws Exception 232 | */ 233 | public function icon(Application $app) { 234 | $response = $this->client->get($this->getUri("query", "icon", $app->getId())); 235 | 236 | if ($response->code !== 200) { 237 | throw new Exception("Command Error - icon"); 238 | } 239 | 240 | return $response->raw_body; 241 | } 242 | 243 | /** 244 | * Launch Application 245 | * 246 | * @param Application $app 247 | * Application 248 | * @param array $params 249 | * Params 250 | * @throws Exception 251 | * 252 | * @return string 253 | */ 254 | public function launch(Application $app, $params = array()) { 255 | $response = $this->client->post($this->getUri("launch", $app->getId()), $params); 256 | 257 | if ($response->code !== 200) { 258 | if ($response->code == 204) { 259 | throw new Exception("Application already launched"); 260 | } 261 | elseif ($response->code == 204) { 262 | throw new Exception("Application Not Found"); 263 | } 264 | else { 265 | throw new Exception("Command Error - launch"); 266 | } 267 | } 268 | 269 | return $response->raw_body; 270 | } 271 | 272 | /** 273 | * Send raw input command 274 | * 275 | * @param array $params 276 | * @throws Exception 277 | * @return string 278 | */ 279 | public function input($params = array()) { 280 | $response = $this->client->post($this->getUri("input"), $params); 281 | 282 | if ($response->code !== 200) { 283 | throw new Exception("Command Error - input"); 284 | } 285 | 286 | return $response->raw_body; 287 | } 288 | 289 | /** 290 | * Touch 291 | * 292 | * @param string $x 293 | * @param string $y 294 | * @param string $op 295 | * 296 | * @throws Exception 297 | * @return string 298 | */ 299 | public function touch($x =0, $y = 0, $op = Touch::DOWN) { 300 | $params = array(); 301 | 302 | if (!Touch::hasName($op)) { 303 | throw new Exception("Touch Option Not Found - " . $op); 304 | } 305 | 306 | $params = array( 307 | "touch.0.x" => $x, 308 | "touch.0.y" => $y, 309 | "touch.0.op" => $op 310 | ); 311 | 312 | return $this->input($params); 313 | } 314 | 315 | /** 316 | * Build uri from input args 317 | * 318 | * @return string 319 | */ 320 | private function getUri() { 321 | $uri = $this->host . ":" . $this->port; 322 | 323 | foreach (func_get_args() as $part) { 324 | $uri .= '/' . $part; 325 | } 326 | 327 | return $uri; 328 | } 329 | 330 | /** 331 | * Execute sleep 332 | * @return void 333 | */ 334 | private function delay() { 335 | if($this->delay > 0) { 336 | sleep($this->delay); 337 | } 338 | } 339 | 340 | /** 341 | * toString 342 | * @return string 343 | */ 344 | public function __toString() { 345 | return "Roku [" . $this->host . ":" . $this->port ."]"; 346 | } 347 | } -------------------------------------------------------------------------------- /src/Roku/Utils/AbstractEnum.php: -------------------------------------------------------------------------------- 1 | getConstants(); 28 | } 29 | 30 | return self::$constCache[$class]; 31 | } 32 | 33 | /** 34 | * Check if constant name exists 35 | * 36 | * @param string $name 37 | * @param string $strict 38 | * @return boolean 39 | */ 40 | public static function hasName($name, $strict = false) { 41 | $constants = self::getConstants(); 42 | 43 | if ($strict) { 44 | return array_key_exists($name, $constants); 45 | } 46 | 47 | $keys = array_map('strtolower', array_keys($constants)); 48 | return in_array(strtolower($name), $keys); 49 | } 50 | 51 | /** 52 | * Check if constant value exists 53 | * 54 | * @param string $name 55 | * @param string $strict 56 | * @return boolean 57 | */ 58 | public static function hasValue($value) { 59 | $values = array_values(self::getConstants()); 60 | return in_array($value, $values, $strict = true); 61 | } 62 | } -------------------------------------------------------------------------------- /src/Roku/Utils/Http.php: -------------------------------------------------------------------------------- 1 | buildUri($uri, $params); 14 | 15 | return \Httpful\Request::get($uri)->send(); 16 | } 17 | 18 | /** 19 | * Post Request 20 | * 21 | * @param string $uri 22 | * @return \Httpful\Response 23 | */ 24 | public function post($uri, $params = array()) { 25 | $uri = $this->buildUri($uri, $params); 26 | 27 | return \Httpful\Request::post($uri)->send(); 28 | } 29 | 30 | private function buildUri ($uri, $params = array()) { 31 | if($params) { 32 | return $uri . "?" . http_build_query($params); 33 | } 34 | 35 | return $uri; 36 | } 37 | } -------------------------------------------------------------------------------- /src/Roku/Utils/HttpConsole.php: -------------------------------------------------------------------------------- 1 | output("GET : " . $uri); 19 | 20 | return new \Httpful\Response("", "HTTP/1.1 200 OK\r\n", \Httpful\Request::init()); 21 | } 22 | 23 | /** 24 | * Post Request 25 | * 26 | * @param string $uri 27 | * @return \Httpful\Response 28 | */ 29 | public function post($uri, $params = array()) { 30 | 31 | if($params) { 32 | $uri .= "?" . http_build_query($params); 33 | } 34 | 35 | $this->output("POST : " . $uri); 36 | 37 | return new \Httpful\Response("", "HTTP/1.1 200 OK\r\n", \Httpful\Request::init()); 38 | } 39 | 40 | private function output($string) { 41 | echo $string; 42 | echo "\n"; 43 | } 44 | } -------------------------------------------------------------------------------- /tests/Roku/Commands/CommandTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(Command::hasName("PLAY")); 18 | $this->assertTrue(Command::hasValue("Play")); 19 | 20 | $this->assertFalse(Command::hasName("nosuch")); 21 | $this->assertTrue(Command::hasName("PLAY")); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Roku/Commands/SensorTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(Sensor::hasName("rotation")); 18 | $this->assertFalse(Sensor::hasName("nosensor")); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/Roku/RokuTest.php: -------------------------------------------------------------------------------- 1 | getHttpInstance(); 14 | 15 | $this->roku = new \Roku\Roku($host, 8060, 0.1); 16 | $this->roku->setClient($http); 17 | 18 | parent::setUp(); 19 | } 20 | 21 | public function testCommands() 22 | { 23 | $this->assertNotNull($this->roku->home()); 24 | $this->assertNotNull($this->roku->up()); 25 | $this->assertNotNull($this->roku->down()); 26 | $this->assertNotNull($this->roku->rev()); 27 | $this->assertNotNull($this->roku->select()); 28 | $this->assertNotNull($this->roku->keydown("home")); 29 | $this->assertNotNull($this->roku->keyup("home")); 30 | } 31 | 32 | public function testSensors() 33 | { 34 | $this->assertNotNull($this->roku->acceleration(0.1,0.2,0.3)); 35 | $this->assertNotNull($this->roku->rotation(1,2,3)); 36 | $this->assertNotNull($this->roku->orientation(0.1,0.2,0.3)); 37 | $this->assertNotNull($this->roku->magnetic(0.1,0.2,0.3)); 38 | } 39 | 40 | public function testTouches() 41 | { 42 | $this->assertNotNull($this->roku->touch(0.1,0.2)); 43 | $this->assertNotNull($this->roku->touch(0.1,0.2, "down")); 44 | $this->assertNotNull($this->roku->touch(0.1,0.2, "up")); 45 | $this->assertNotNull($this->roku->touch(0.1,0.2, "move")); 46 | 47 | } 48 | 49 | public function testIcon() 50 | { 51 | $app = new \Roku\Application("dev", "0.1.0", "Test App"); 52 | 53 | $this->assertNotNull($this->roku->icon($app)); 54 | } 55 | 56 | public function testLaunch() 57 | { 58 | $app = new \Roku\Application("dev", "0.1.0", "Test App"); 59 | 60 | $this->assertNotNull($this->roku->launch($app, array("contentID" => 120))); 61 | } 62 | 63 | public function testApplication() 64 | { 65 | $app = new \Roku\Application("dev"); 66 | $app->setId("dev"); 67 | $app->setName("Test App"); 68 | $app->setVersion("0.1.0"); 69 | 70 | $this->assertEquals(new \Roku\Application("dev", "0.1.0", "Test App"), $app); 71 | } 72 | 73 | /** 74 | * @expectedException \Roku\Exception 75 | * @expectedExceptionCode 0 76 | */ 77 | public function testErrors() 78 | { 79 | $this->assertNotNull($this->roku->rotate()); 80 | } 81 | 82 | /** 83 | * @expectedException \Roku\Exception 84 | * @expectedExceptionCode 0 85 | */ 86 | public function testErrorsTouch() 87 | { 88 | $this->assertNotNull($this->roku->touch(1,1,"nosuchtouch")); 89 | } 90 | 91 | public function testApps() 92 | { 93 | $host = "http://127.0.0.1"; 94 | $http = $this->getHttpXmlInstance(); 95 | 96 | $this->roku = new \Roku\Roku($host); 97 | $this->roku->setClient($http); 98 | 99 | $apps = $this->roku->apps(); 100 | 101 | $this->assertEquals(31012, $apps[0]->getId()); 102 | $this->assertEquals("1.4.14", $apps[0]->getVersion()); 103 | $this->assertEquals("Movies and TV Shows", $apps[0]->getName()); 104 | 105 | $this->assertCount(43, $apps); 106 | } 107 | 108 | public function testDevice() 109 | { 110 | $host = "http://127.0.0.1"; 111 | $http = $this->getHttpXmlInstance("roku.xml"); 112 | 113 | $this->roku = new \Roku\Roku($host); 114 | $this->roku->setClient($http); 115 | 116 | $device = $this->roku->device(); 117 | 118 | $this->assertEquals("urn:roku-com:device:player:1-0", $device->getDeviceType()); 119 | $this->assertEquals("Roku Streaming Player", $device->getFriendlyName()); 120 | $this->assertEquals("Roku", $device->getManufacturer()); 121 | $this->assertEquals("http://www.roku.com/", $device->getManufacturerURL()); 122 | $this->assertEquals("Roku Streaming Player Network Media", $device->getModelDescription()); 123 | $this->assertEquals("Roku Streaming Player 4200X", $device->getModelName()); 124 | $this->assertEquals("4200X", $device->getModelNumber()); 125 | $this->assertEquals("http://www.roku.com/", $device->getModelURL()); 126 | $this->assertEquals("uuid:8888888-8888-888-8888-b83e59cfd25f", $device->getUDN()); 127 | $this->assertEquals("1GN111111111", $device->getSerialNumber()); 128 | } 129 | 130 | public function getHttpInstance () { 131 | $headers = 132 | "HTTP/1.1 200 OK 133 | Content-Length: 0 134 | Server: Roku UPnP/1.0 MiniUPnPd/1.4\r\n"; 135 | 136 | $response = new \Httpful\Response("", $headers, \Httpful\Request::init()); 137 | 138 | $http = $this->createMock('\Roku\Utils\Http'); 139 | 140 | // Configure the stub. 141 | $http->expects($this->any()) 142 | ->method('get') 143 | ->will($this->returnValue($response)); 144 | 145 | $http->expects($this->any()) 146 | ->method('post') 147 | ->will($this->returnValue($response)); 148 | 149 | return $http; 150 | } 151 | 152 | public function getHttpXmlInstance ($file = "apps.xml") { 153 | $headers = 154 | "HTTP/1.1 200 OK 155 | Content-Length: 0 156 | Server: Roku UPnP/1.0 MiniUPnPd/1.4\r\n"; 157 | 158 | $response = new \Httpful\Response( file_get_contents(__DIR__."/../data/" . $file), $headers, \Httpful\Request::init()); 159 | 160 | $http = $this->createMock('\Roku\Utils\Http'); 161 | 162 | // Configure the stub. 163 | $http->expects($this->any()) 164 | ->method('get') 165 | ->will($this->returnValue($response)); 166 | 167 | $http->expects($this->any()) 168 | ->method('post') 169 | ->will($this->returnValue($response)); 170 | 171 | return $http; 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Movies and TV Shows 6 | 7 | 8 | Roku Home News 9 | 10 | 11 | Netflix 12 | 13 | 14 | Time Warner Cable 15 | 16 | 17 | Amazon Instant Video 18 | 19 | 20 | Hulu Plus 21 | 22 | 23 | Redbox Instant 24 | 25 | 26 | Target Ticket 27 | 28 | 29 | VUDU 30 | 31 | 32 | Blockbuster 33 | 34 | 35 | VEVO 36 | 37 | 38 | Slacker 39 | 40 | 41 | WatchESPN 42 | 43 | 44 | WSJ Live 45 | 46 | 47 | Angry Birds Space 48 | 49 | 50 | FOX NOW 51 | 52 | 53 | HBO GO 54 | 55 | 56 | MOG music 57 | 58 | 59 | iHeartRadio 60 | 61 | 62 | Rdio 63 | 64 | 65 | Angry Birds 66 | 67 | 68 | Christmas Winter 69 | 70 | 71 | YouTube 72 | 73 | 74 | Amazon Cloud Player 75 | 76 | 77 | Pandora 78 | 79 | 80 | Spotify 81 | 82 | 83 | PBS KIDS 84 | 85 | 86 | PAC-MAN Championship Edition 87 | 88 | 89 | Baeble 90 | 91 | 92 | WWE Network 93 | 94 | 95 | Lifetime 96 | 97 | 98 | Disney 99 | 100 | 101 | TDN 102 | 103 | 104 | Anna Montana Demo 105 | 106 | 107 | Facebook Photos & Videos 108 | 109 | 110 | NHL GameCenter™ 111 | 112 | 113 | PopcornFlix Kids 114 | 115 | 116 | HappyKids 117 | 118 | 119 | Disney Channel 120 | 121 | 122 | Disney Junior 123 | 124 | 125 | HISTORY 126 | 127 | 128 | National Geographic Kids 129 | 130 | 131 | TestApp 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /tests/data/roku.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | 0 6 | 7 | 8 | urn:roku-com:device:player:1-0 9 | Roku Streaming Player 10 | Roku 11 | http://www.roku.com/ 12 | Roku Streaming Player Network Media 13 | Roku Streaming Player 4200X 14 | 4200X 15 | http://www.roku.com/ 16 | 1GN111111111 17 | uuid:8888888-8888-888-8888-b83e59cfd25f 18 | 19 | 20 | urn:roku-com:service:ecp:1 21 | urn:roku-com:serviceId:ecp1-0 22 | 23 | 24 | ecp_SCPD.xml 25 | 26 | 27 | urn:dial-multiscreen-org:service:dial:1 28 | urn:dial-multiscreen-org:serviceId:dial1-0 29 | 30 | 31 | dial_SCPD.xml 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /tests/data/samples.txt: -------------------------------------------------------------------------------- 1 | 2 | http POST http://192.168.72.34:8060/launch/dev? 3 | HTTP/1.1 200 OK 4 | Content-Length: 0 5 | Server: Roku UPnP/1.0 MiniUPnPd/1.4 6 | 7 | 8 | 9 | http POST http://192.168.72.34:8060/launch/dev? 10 | HTTP/1.1 204 No Content 11 | Server: Roku UPnP/1.0 MiniUPnPd/1.4 12 | 13 | http POST http://192.168.72.34:8060/keypress/home 14 | HTTP/1.1 200 OK 15 | Content-Length: 0 16 | Server: Roku UPnP/1.0 MiniUPnPd/1.4 17 | --------------------------------------------------------------------------------