├── .gitignore ├── LICENSE ├── README.md ├── control_packet.txt ├── index.js └── panasonicviera.js /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | *.sublime-* 10 | *.php 11 | 12 | pids 13 | logs 14 | results 15 | 16 | npm-debug.log -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Tom Haskell 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 13 | all 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 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-panasonic-viera 2 | 3 | Node.js module for control of a Panasonic Viera TV 4 | 5 | ## How to use 6 | 7 | 1. Require the module: 8 | 9 | ```js 10 | var PanasonicViera = require('./panasonicviera'); 11 | ``` 12 | 13 | 2. Create an instance: 14 | 15 | ```js 16 | var tv = new PanasonicViera(''); 17 | ``` 18 | 19 | 3. Start sending messages! 20 | 21 | ```js 22 | tv.send(PanasonicViera.POWER_TOGGLE); 23 | 24 | tv.setVolume(20); 25 | ``` 26 | 27 | You can see an example in ``` index.js ``` 28 | 29 | ## License 30 | The MIT License (MIT) 31 | 32 | Copyright (c) 2013 Tom Haskell 33 | 34 | Permission is hereby granted, free of charge, to any person obtaining a copy 35 | of this software and associated documentation files (the "Software"), to deal 36 | in the Software without restriction, including without limitation the rights 37 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 38 | copies of the Software, and to permit persons to whom the Software is 39 | furnished to do so, subject to the following conditions: 40 | 41 | The above copyright notice and this permission notice shall be included in 42 | all copies or substantial portions of the Software. 43 | 44 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 45 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 46 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 47 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 48 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 49 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 50 | THE SOFTWARE. -------------------------------------------------------------------------------- /control_packet.txt: -------------------------------------------------------------------------------- 1 | POST /nrc/control_0 HTTP/1.1 2 | User-Agent: Panasonic Android VR-CP UPnP/2.0 3 | Host: 192.168.1.101:55000 4 | Content-Type: text/xml; charset="utf-8" 5 | SOAPACTION: "urn:panasonic-com:service:p00NetworkControl:1#X_SendKey" 6 | Content-Length: 329 7 | 8 | 9 | 10 | 11 | 12 | NRC_TV-ONOFF 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Usage file for PanasonicViera module 3 | */ 4 | 5 | var PanasonicViera = require('./panasonicviera'); 6 | var readline = require('readline'); 7 | 8 | // create instance of module 9 | var tv = new PanasonicViera('192.168.1.101'); 10 | 11 | // get mute value 12 | tv.getMute(function(data){ 13 | console.log('current mute: '+data); 14 | 15 | }); 16 | 17 | // set volume to 20 18 | tv.setVolume(20); 19 | 20 | 21 | // get key press commands from command line 22 | var rl = readline.createInterface({ 23 | input: process.stdin, 24 | output: process.stdout 25 | }); 26 | 27 | read = function(){ 28 | rl.question("Command: ", function(command) { 29 | if(command == ''){ 30 | rl.close(); 31 | }else{ 32 | tv.send(command); 33 | read(); 34 | } 35 | }); 36 | } 37 | 38 | read(); 39 | 40 | -------------------------------------------------------------------------------- /panasonicviera.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Node.js module for control of a Panasonic Viera TV 3 | * 4 | * Copyright (c) 2013 THLabs.net 5 | */ 6 | 7 | var http = require('http'); 8 | 9 | //Export the constructor 10 | exports = module.exports = PanasonicViera; 11 | 12 | /* 13 | * Key Constants 14 | */ 15 | // power 16 | exports.POWER_TOGGLE = 'POWER'; 17 | // channel 18 | exports.CHANNEL_DOWN = 'CH_DOWN'; 19 | exports.CHANNEL_UP = 'CH_UP'; 20 | // volume 21 | exports.VOLUME_UP = 'VOLUP'; 22 | exports.VOLUME_DOWN = 'VOLDOWN'; 23 | exports.MUTE_TOGGLE = 'MUTE'; 24 | // source control 25 | exports.TV = 'TV'; 26 | exports.INTERNET = 'INTERNET'; 27 | exports.CHANGE_INPUT = 'CHG_INPUT'; 28 | exports.SD_CARD = 'SD_CARD'; 29 | // number keypad 30 | exports.D1 = 'D1'; 31 | exports.D2 = 'D2'; 32 | exports.D3 = 'D3'; 33 | exports.D4 = 'D4'; 34 | exports.D5 = 'D5'; 35 | exports.D6 = 'D6'; 36 | exports.D7 = 'D7'; 37 | exports.D8 = 'D8'; 38 | exports.D9 = 'D9'; 39 | exports.D0 = 'D0'; 40 | // arrow keypad 41 | exports.MENU = 'MENU'; 42 | exports.SUBMENU = 'SUBMENU'; 43 | exports.RETURN = 'RETURN'; 44 | exports.ENTER = 'ENTER'; 45 | exports.RIGHT = 'RIGHT'; 46 | exports.LEFT = 'LEFT'; 47 | exports.UP = 'UP'; 48 | exports.DOWN = 'DOWN'; 49 | exports.DISP_MODE = 'DISP_MODE'; 50 | exports.CANCEL = 'CANCEL'; 51 | exports.INDEX = 'INDEX'; 52 | // coloured buttons 53 | exports.RED = 'RED'; 54 | exports.GREEN = 'GREEN'; 55 | exports.YELLOW = 'YELLOW'; 56 | exports.BLUE = 'BLUE'; 57 | // programme info 58 | exports.GUIDE = 'EPG'; 59 | exports.TEXT = 'TEXT'; 60 | exports.INFO = 'INFO'; 61 | // playback control 62 | exports.REW = 'REW'; 63 | exports.PLAY = 'PLAY'; 64 | exports.FF = 'FF'; 65 | exports.SKIP_PREV = 'SKIP_PREV'; 66 | exports.PAUSE = 'PAUSE'; 67 | exports.SKIP_NEXT = 'SKIP_NEXT'; 68 | exports.STOP = 'STOP'; 69 | exports.REC = 'REC'; 70 | // misc 71 | exports.VTOOLS = 'VTOOLS'; 72 | exports.VIERA_LINK = 'VIERA_LINK'; 73 | exports.STTL = 'STTL'; 74 | exports.HOLD = 'HOLD'; 75 | exports.R_TUNE = 'R_TUNE'; 76 | exports._3D = '3D'; 77 | 78 | /* 79 | * Internal Constants 80 | */ 81 | // urls 82 | var URL_NETWORK = '/nrc/control_0'; 83 | var URL_RENDERING = '/dmr/control_0'; 84 | 85 | // urns 86 | var URN_NETWORK = 'panasonic-com:service:p00NetworkControl:1'; 87 | var URN_RENDERING = 'schemas-upnp-org:service:RenderingControl:1'; 88 | 89 | // actions 90 | var ACTION_SENDKEY = 'X_SendKey'; 91 | var ACTION_GETVOLUME = 'GetVolume'; 92 | var ACTION_SETVOLUME = 'SetVolume'; 93 | var ACTION_GETMUTE = 'GetMute'; 94 | var ACTION_SETMUTE = 'SetMute'; 95 | 96 | // args 97 | var ARG_SENDKEY = 'X_KeyEvent'; 98 | 99 | /* 100 | * Variables 101 | */ 102 | var ipAddress = "0.0.0.0"; 103 | 104 | 105 | /** 106 | * PanasonicViera contructor 107 | * 108 | * @param {string} ipAddress The IP Address of the TV 109 | */ 110 | function PanasonicViera(ipAddress){ 111 | this.ipAddress = ipAddress; 112 | } 113 | 114 | /** 115 | * Create and submit XML request to the TV 116 | * 117 | * @param {String} url The API endpoint required for this 118 | * @param {String} urn The URN required for this 119 | * @param {String} action The action type to perform 120 | * @param {Array} option Options array - use ['args'] for action specific events 121 | */ 122 | PanasonicViera.prototype.submitRequest = function(url, urn, action, options){ 123 | var self = this; 124 | var command_str = "\n"+ 125 | "\n"+ 126 | " \n"+ 127 | " \n"+ 128 | " "+options['args']+"\n"+ 129 | " \n"+ 130 | " \n"+ 131 | "\n"; 132 | 133 | var post_options = { 134 | host: this.ipAddress, 135 | port: '55000', 136 | path: url, 137 | method: 'POST', 138 | headers: { 139 | 'Content-Length': command_str.length, 140 | 'Content-Type': 'text/xml; charset="utf-8"', 141 | 'User-Agent': 'net.thlabs.nodecontrol', 142 | 'SOAPACTION': '"urn:'+urn+'#'+action+'"' 143 | } 144 | } 145 | 146 | if(options.hasOwnProperty('callback')){ 147 | self.callback = options['callback']; 148 | }else{ 149 | self.callback = function(data){ console.log(data) }; 150 | } 151 | 152 | //console.log(command_str); 153 | 154 | var req = http.request(post_options, function(res) { 155 | //console.log('STATUS: ' + res.statusCode); 156 | //console.log('HEADERS: ' + JSON.stringify(res.headers)); 157 | res.setEncoding('utf8'); 158 | res.on('data', self.callback); 159 | }); 160 | req.on('error', function(e) { 161 | console.log('error: ' + e.message); 162 | console.log(e); 163 | }); 164 | 165 | req.write(command_str); 166 | req.end(); 167 | 168 | } 169 | 170 | /** 171 | * Send a key state event to the TV 172 | * 173 | * @param {String} key The key value to send 174 | * @param {String} state 175 | */ 176 | PanasonicViera.prototype.sendKey = function(key, state){ 177 | this.submitRequest( 178 | URL_NETWORK, 179 | URN_NETWORK, 180 | ACTION_SENDKEY, 181 | { 182 | args: "<"+ARG_SENDKEY+">NRC_"+key+"-"+state+"" 183 | } 184 | ); 185 | } 186 | /** 187 | * Send a key press event to the TV 188 | * 189 | * @param {String} key The key that has been pressed 190 | */ 191 | PanasonicViera.prototype.keyDown = function(key){ 192 | this.sendKey(key,'ON'); 193 | } 194 | /** 195 | * Send a key release event to the TV 196 | * 197 | * @param {String} key The key that has been released 198 | */ 199 | PanasonicViera.prototype.keyUp = function(key){ 200 | this.sendKey(key,'OFF'); 201 | } 202 | /** 203 | * Send a key push/release event to the TV 204 | * 205 | * @param {String} key The key that has been released 206 | */ 207 | PanasonicViera.prototype.send = function(key){ 208 | this.sendKey(key,'ONOFF'); 209 | } 210 | 211 | /** 212 | * Get the current volume value 213 | * 214 | * @param {Function} callback A function of the form function(volume) to return the volume value to 215 | */ 216 | PanasonicViera.prototype.getVolume = function(callback){ 217 | self = this; 218 | self.volCallback = callback; 219 | this.submitRequest( 220 | URL_RENDERING, 221 | URN_RENDERING, 222 | ACTION_GETVOLUME, 223 | { 224 | args: "0Master", 225 | callback: function(data){ 226 | var regex = /(\d*)<\/CurrentVolume>/gm; 227 | var match = regex.exec(data); 228 | if(match !== null){ 229 | var volume = match[1]; 230 | self.volCallback(volume); 231 | } 232 | } 233 | } 234 | ); 235 | } 236 | /** 237 | * Set the volume to specific level 238 | * 239 | * @param {int} volume The value to set the volume to 240 | */ 241 | PanasonicViera.prototype.setVolume = function(volume){ 242 | this.submitRequest( 243 | URL_RENDERING, 244 | URN_RENDERING, 245 | ACTION_SETVOLUME, 246 | { 247 | args: "0Master"+volume+"" 248 | } 249 | ); 250 | } 251 | 252 | /** 253 | * Get the current mute setting 254 | * 255 | * @param {Function} callback A function of the form function(mute) to return the volume value to 256 | */ 257 | PanasonicViera.prototype.getMute = function(callback){ 258 | self = this; 259 | self.muteCallback = callback; 260 | this.submitRequest( 261 | URL_RENDERING, 262 | URN_RENDERING, 263 | ACTION_GETMUTE, 264 | { 265 | args: "0Master", 266 | callback: function(data){ 267 | var regex = /([0-1])<\/CurrentMute>/gm; 268 | var match = regex.exec(data); 269 | if(match !== null){ 270 | var mute = (match[1] == '1'); 271 | self.muteCallback(mute); 272 | } 273 | } 274 | } 275 | ); 276 | } 277 | /** 278 | * Set mute to on/off 279 | * 280 | * @param {boolean} enable The value to set mute to 281 | */ 282 | PanasonicViera.prototype.setMute = function(enable){ 283 | var data = (enable)? '1' : '0'; 284 | this.submitRequest( 285 | URL_RENDERING, 286 | URN_RENDERING, 287 | ACTION_SETMUTE, 288 | { 289 | args: "0Master"+data+"" 290 | } 291 | ); 292 | } --------------------------------------------------------------------------------