├── .gitignore ├── .npmignore ├── README.md ├── examples └── basic.js ├── index.js ├── package.json └── src ├── lib ├── cectypes.coffee └── emitLines.coffee └── node-cec.coffee /.gitignore: -------------------------------------------------------------------------------- 1 | #- PROJECT 2 | 3 | lib 4 | 5 | 6 | # ---------------------------------------------------------------------------- # 7 | # NODE ~ https://github.com/github/gitignore/blob/master/Node.gitignore 8 | 9 | # Logs 10 | logs 11 | *.log 12 | npm-debug.log* 13 | 14 | # Runtime data 15 | pids 16 | *.pid 17 | *.seed 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (http://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules 39 | jspm_packages 40 | 41 | # Optional npm cache directory 42 | .npm 43 | 44 | # Optional REPL history 45 | .node_repl_history 46 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-cec 2 | cec-client wrapper in nodejs 3 | 4 | ## Install 5 | 6 | ```bash 7 | npm install --save node-cec 8 | ``` 9 | 10 | ## Example 11 | 12 | #### Output 13 | 14 | ![](https://cloud.githubusercontent.com/assets/4481570/15092052/2fce287a-145e-11e6-928b-9ed1fa9caadb.jpg) 15 | 16 | #### Source 17 | 18 | ```javascript 19 | // -------------------------------------------------------------------------- // 20 | // Example: basic.js 21 | // For more cec-events: http://www.cec-o-matic.com/ 22 | // -------------------------------------------------------------------------- // 23 | 24 | var nodecec = require( 'node-cec' ); 25 | 26 | var NodeCec = nodecec.NodeCec; 27 | var CEC = nodecec.CEC; 28 | 29 | var cec = new NodeCec( 'node-cec-monitor' ); 30 | 31 | 32 | // -------------------------------------------------------------------------- // 33 | //- KILL CEC-CLIENT PROCESS ON EXIT 34 | 35 | process.on( 'SIGINT', function() { 36 | if ( cec != null ) { 37 | cec.stop(); 38 | } 39 | process.exit(); 40 | }); 41 | 42 | 43 | // -------------------------------------------------------------------------- // 44 | //- CEC EVENT HANDLING 45 | 46 | cec.once( 'ready', function(client) { 47 | console.log( ' -- READY -- ' ); 48 | client.sendCommand( 0xf0, CEC.Opcode.GIVE_DEVICE_POWER_STATUS ); 49 | }); 50 | 51 | cec.on( 'REPORT_POWER_STATUS', function (packet, status) { 52 | var keys = Object.keys( CEC.PowerStatus ); 53 | 54 | for (var i = keys.length - 1; i >= 0; i--) { 55 | if (CEC.PowerStatus[keys[i]] == status) { 56 | console.log('POWER_STATUS:', keys[i]); 57 | break; 58 | } 59 | } 60 | 61 | }); 62 | 63 | cec.on( 'ROUTING_CHANGE', function(packet, fromSource, toSource) { 64 | console.log( 'Routing changed from ' + fromSource + ' to ' + toSource + '.' ); 65 | }); 66 | 67 | 68 | // -------------------------------------------------------------------------- // 69 | //- START CEC CLIENT 70 | 71 | // -m = start in monitor-mode 72 | // -d8 = set log level to 8 (=TRAFFIC) (-d 8) 73 | // -br = logical address set to `recording device` 74 | cec.start( 'cec-client', '-m', '-d', '8', '-b', 'r' ); 75 | ``` 76 | 77 | 78 | 79 | # License 80 | 81 | The MIT License (MIT) 82 | 83 | Copyright (c) 2015 Patrick Wozniak 84 | 85 | Permission is hereby granted, free of charge, to any person obtaining a copy 86 | of this software and associated documentation files (the "Software"), to deal 87 | in the Software without restriction, including without limitation the rights 88 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 89 | copies of the Software, and to permit persons to whom the Software is 90 | furnished to do so, subject to the following conditions: 91 | 92 | The above copyright notice and this permission notice shall be included in 93 | all copies or substantial portions of the Software. 94 | 95 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 96 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 97 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 98 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 99 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 100 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 101 | THE SOFTWARE. 102 | -------------------------------------------------------------------------------- /examples/basic.js: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------------------- // 2 | // Example: basic.js 3 | // For more cec-events: http://www.cec-o-matic.com/ 4 | // -------------------------------------------------------------------------- // 5 | 6 | var nodecec = require( '../' ); 7 | 8 | var NodeCec = nodecec.NodeCec; 9 | var CEC = nodecec.CEC; 10 | 11 | var cec = new NodeCec( 'node-cec-monitor' ); 12 | 13 | 14 | // -------------------------------------------------------------------------- // 15 | //- KILL CEC-CLIENT PROCESS ON EXIT 16 | 17 | process.on( 'SIGINT', function() { 18 | if ( cec != null ) { 19 | cec.stop(); 20 | } 21 | process.exit(); 22 | }); 23 | 24 | 25 | // -------------------------------------------------------------------------- // 26 | //- CEC EVENT HANDLING 27 | 28 | cec.once( 'ready', function(client) { 29 | console.log( ' -- READY -- ' ); 30 | client.sendCommand( 0xf0, CEC.Opcode.GIVE_DEVICE_POWER_STATUS ); 31 | }); 32 | 33 | cec.on( 'REPORT_POWER_STATUS', function (packet, status) { 34 | var keys = Object.keys( CEC.PowerStatus ); 35 | 36 | for (var i = keys.length - 1; i >= 0; i--) { 37 | if (CEC.PowerStatus[keys[i]] == status) { 38 | console.log('POWER_STATUS:', keys[i]); 39 | break; 40 | } 41 | } 42 | 43 | }); 44 | 45 | cec.on( 'ROUTING_CHANGE', function(packet, fromSource, toSource) { 46 | console.log( 'Routing changed from ' + fromSource + ' to ' + toSource + '.' ); 47 | }); 48 | 49 | 50 | // -------------------------------------------------------------------------- // 51 | //- START CEC CLIENT 52 | 53 | // -m = start in monitor-mode 54 | // -d8 = set log level to 8 (=TRAFFIC) (-d 8) 55 | // -br = logical address set to `recording device` 56 | cec.start( 'cec-client', '-m', '-d', '8', '-b', 'r' ); 57 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require( './lib/node-cec' ) 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-cec", 3 | "version": "0.1.3", 4 | "description": "cec-client wrapper", 5 | "author": "Patrick Wozniak ", 6 | "main": "index.js", 7 | "keywords": [ 8 | "cec", 9 | "hdmi" 10 | ], 11 | "scripts": { 12 | "lint": "coffeelint src/", 13 | "pretest": "npm run lint", 14 | "test": "echo 'no tests'", 15 | "prebuild": "npm run test", 16 | "build": "coffee -o lib/ -c src/", 17 | "watch": "coffee -o lib/ -cw src/", 18 | "prepublish": "npm run build" 19 | }, 20 | "devDependencies": { 21 | "coffee-script": "1.10.0", 22 | "coffeelint": "1.15.7" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "git://github.com/patlux/node-cec.git" 27 | }, 28 | "license": "MIT", 29 | "coffeelintConfig": { 30 | "indentation": { 31 | "level": "error", 32 | "value": 2 33 | }, 34 | "line_endings": { 35 | "value": "unix", 36 | "level": "error" 37 | }, 38 | "max_line_length": { 39 | "level": "warn" 40 | }, 41 | "no_trailing_whitespace": { 42 | "level": "warn" 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/lib/cectypes.coffee: -------------------------------------------------------------------------------- 1 | # from: https://github.com/Pulse-Eight/libcec/blob/master/include/cectypes.h 2 | 3 | module.exports = 4 | 5 | VendorId: 6 | TOSHIBA: 0x000039 7 | SAMSUNG: 0x0000F0 8 | DENON: 0x0005CD 9 | MARANTZ: 0x000678 10 | LOEWE: 0x000982 11 | ONKYO: 0x0009B0 12 | MEDION: 0x000CB8 13 | TOSHIBA2: 0x000CE7 14 | PULSE_EIGHT: 0x001582 15 | HARMAN_KARDON2: 0x001950 16 | GOOGLE: 0x001A11 17 | AKAI: 0x0020C7 18 | AOC: 0x002467 19 | PANASONIC: 0x008045 20 | PHILIPS: 0x00903E 21 | DAEWOO: 0x009053 22 | YAMAHA: 0x00A0DE 23 | GRUNDIG: 0x00D0D5 24 | PIONEER: 0x00E036 25 | LG: 0x00E091 26 | SHARP: 0x08001F 27 | SONY: 0x080046 28 | BROADCOM: 0x18C086 29 | VIZIO: 0x6B746D 30 | BENQ: 0x8065E9 31 | HARMAN_KARDON: 0x9C645E 32 | UNKNOWN: 0 33 | 34 | LogicalAddress: 35 | UNKNOWN: -1 # not a valid logical address 36 | TV: 0 37 | RECORDINGDEVICE1: 1 38 | RECORDINGDEVICE2: 2 39 | TUNER1: 3 40 | PLAYBACKDEVICE1: 4 41 | AUDIOSYSTEM: 5 42 | TUNER2: 6 43 | TUNER3: 7 44 | PLAYBACKDEVICE2: 8 45 | RECORDINGDEVICE3: 9 46 | TUNER4: 10 47 | PLAYBACKDEVICE3: 11 48 | RESERVED1: 12 49 | RESERVED2: 13 50 | FREEUSE: 14 51 | UNREGISTERED: 15 # for source 52 | BROADCAST: 15 # for target 53 | 54 | 55 | Opcode: 56 | ACTIVE_SOURCE: 0x82 57 | IMAGE_VIEW_ON: 0x04 58 | TEXT_VIEW_ON: 0x0D 59 | INACTIVE_SOURCE: 0x9D 60 | REQUEST_ACTIVE_SOURCE: 0x85 61 | ROUTING_CHANGE: 0x80 62 | ROUTING_INFORMATION: 0x81 63 | SET_STREAM_PATH: 0x86 64 | STANDBY: 0x36 65 | RECORD_OFF: 0x0B 66 | RECORD_ON: 0x09 67 | RECORD_STATUS: 0x0A 68 | RECORD_TV_SCREEN: 0x0F 69 | CLEAR_ANALOGUE_TIMER: 0x33 70 | CLEAR_DIGITAL_TIMER: 0x99 71 | CLEAR_EXTERNAL_TIMER: 0xA1 72 | SET_ANALOGUE_TIMER: 0x34 73 | SET_DIGITAL_TIMER: 0x97 74 | SET_EXTERNAL_TIMER: 0xA2 75 | SET_TIMER_PROGRAM_TITLE: 0x67 76 | TIMER_CLEARED_STATUS: 0x43 77 | TIMER_STATUS: 0x35 78 | CEC_VERSION: 0x9E 79 | GET_CEC_VERSION: 0x9F 80 | GIVE_PHYSICAL_ADDRESS: 0x83 81 | GET_MENU_LANGUAGE: 0x91 82 | REPORT_PHYSICAL_ADDRESS: 0x84 83 | SET_MENU_LANGUAGE: 0x32 84 | DECK_CONTROL: 0x42 85 | DECK_STATUS: 0x1B 86 | GIVE_DECK_STATUS: 0x1A 87 | PLAY: 0x41 88 | GIVE_TUNER_DEVICE_STATUS: 0x08 89 | SELECT_ANALOGUE_SERVICE: 0x92 90 | SELECT_DIGITAL_SERVICE: 0x93 91 | TUNER_DEVICE_STATUS: 0x07 92 | TUNER_STEP_DECREMENT: 0x06 93 | TUNER_STEP_INCREMENT: 0x05 94 | DEVICE_VENDOR_ID: 0x87 95 | GIVE_DEVICE_VENDOR_ID: 0x8C 96 | VENDOR_COMMAND: 0x89 97 | VENDOR_COMMAND_WITH_ID: 0xA0 98 | VENDOR_REMOTE_BUTTON_DOWN: 0x8A 99 | VENDOR_REMOTE_BUTTON_UP: 0x8B 100 | SET_OSD_STRING: 0x64 101 | GIVE_OSD_NAME: 0x46 102 | SET_OSD_NAME: 0x47 103 | MENU_REQUEST: 0x8D 104 | MENU_STATUS: 0x8E 105 | USER_CONTROL_PRESSED: 0x44 106 | USER_CONTROL_RELEASE: 0x45 107 | GIVE_DEVICE_POWER_STATUS: 0x8F 108 | REPORT_POWER_STATUS: 0x90 109 | FEATURE_ABORT: 0x00 110 | ABORT: 0xFF 111 | GIVE_AUDIO_STATUS: 0x71 112 | GIVE_SYSTEM_AUDIO_MODE_STATUS: 0x7D 113 | REPORT_AUDIO_STATUS: 0x7A 114 | SET_SYSTEM_AUDIO_MODE: 0x72 115 | SYSTEM_AUDIO_MODE_REQUEST: 0x70 116 | SYSTEM_AUDIO_MODE_STATUS: 0x7E 117 | SET_AUDIO_RATE: 0x9A 118 | 119 | # CEC 1.4 120 | START_ARC: 0xC0 121 | REPORT_ARC_STARTED: 0xC1 122 | REPORT_ARC_ENDED: 0xC2 123 | REQUEST_ARC_START: 0xC3 124 | REQUEST_ARC_END: 0xC4 125 | END_ARC: 0xC5 126 | CDC: 0xF8 127 | # when this opcode is set no opcode will be sent to the device. this is one of the reserved numbers 128 | NONE: 0xFD 129 | 130 | 131 | UserControlCode: 132 | SELECT: 0x00 133 | UP: 0x01 134 | DOWN: 0x02 135 | LEFT: 0x03 136 | RIGHT: 0x04 137 | RIGHT_UP: 0x05 138 | RIGHT_DOWN: 0x06 139 | LEFT_UP: 0x07 140 | LEFT_DOWN: 0x08 141 | ROOT_MENU: 0x09 142 | SETUP_MENU: 0x0A 143 | CONTENTS_MENU: 0x0B 144 | FAVORITE_MENU: 0x0C 145 | EXIT: 0x0D 146 | # reserved: 0x0E, 0x0F 147 | TOP_MENU: 0x10 148 | DVD_MENU: 0x11 149 | # reserved: 0x12 ... 0x1C 150 | NUMBER_ENTRY_MODE: 0x1D 151 | NUMBER11: 0x1E 152 | NUMBER12: 0x1F 153 | NUMBER0: 0x20 154 | NUMBER1: 0x21 155 | NUMBER2: 0x22 156 | NUMBER3: 0x23 157 | NUMBER4: 0x24 158 | NUMBER5: 0x25 159 | NUMBER6: 0x26 160 | NUMBER7: 0x27 161 | NUMBER8: 0x28 162 | NUMBER9: 0x29 163 | DOT: 0x2A 164 | ENTER: 0x2B 165 | CLEAR: 0x2C 166 | NEXT_FAVORITE: 0x2F 167 | CHANNEL_UP: 0x30 168 | CHANNEL_DOWN: 0x31 169 | PREVIOUS_CHANNEL: 0x32 170 | SOUND_SELECT: 0x33 171 | INPUT_SELECT: 0x34 172 | DISPLAY_INFORMATION: 0x35 173 | HELP: 0x36 174 | PAGE_UP: 0x37 175 | PAGE_DOWN: 0x38 176 | # reserved: 0x39 ... 0x3F 177 | POWER: 0x40 178 | VOLUME_UP: 0x41 179 | VOLUME_DOWN: 0x42 180 | MUTE: 0x43 181 | PLAY: 0x44 182 | STOP: 0x45 183 | PAUSE: 0x46 184 | RECORD: 0x47 185 | REWIND: 0x48 186 | FAST_FORWARD: 0x49 187 | EJECT: 0x4A 188 | FORWARD: 0x4B 189 | BACKWARD: 0x4C 190 | STOP_RECORD: 0x4D 191 | PAUSE_RECORD: 0x4E 192 | # reserved: 0x4F 193 | ANGLE: 0x50 194 | SUB_PICTURE: 0x51 195 | VIDEO_ON_DEMAND: 0x52 196 | ELECTRONIC_PROGRAM_GUIDE: 0x53 197 | TIMER_PROGRAMMING: 0x54 198 | INITIAL_CONFIGURATION: 0x55 199 | SELECT_BROADCAST_TYPE: 0x56 200 | SELECT_SOUND_PRESENTATION: 0x57 201 | # reserved: 0x58 ... 0x5F 202 | PLAY_FUNCTION: 0x60 203 | PAUSE_PLAY_FUNCTION: 0x61 204 | RECORD_FUNCTION: 0x62 205 | PAUSE_RECORD_FUNCTION: 0x63 206 | STOP_FUNCTION: 0x64 207 | MUTE_FUNCTION: 0x65 208 | RESTORE_VOLUME_FUNCTION: 0x66 209 | TUNE_FUNCTION: 0x67 210 | SELECT_MEDIA_FUNCTION: 0x68 211 | SELECT_AV_INPUT_FUNCTION: 0x69 212 | SELECT_AUDIO_INPUT_FUNCTION: 0x6A 213 | POWER_TOGGLE_FUNCTION: 0x6B 214 | POWER_OFF_FUNCTION: 0x6C 215 | POWER_ON_FUNCTION: 0x6D 216 | # reserved: 0x6E ... 0x70 217 | F1_BLUE: 0x71 218 | F2_RED: 0x72 219 | F3_GREEN: 0x73 220 | F4_YELLOW: 0x74 221 | F5: 0x75 222 | DATA: 0x76 223 | # reserved: 0x77 ... 0xFF 224 | AN_RETURN: 0x91 # return (Samsung) 225 | AN_CHANNELS_LIST: 0x96 # channels list (Samsung) 226 | MAX: 0x96 227 | UNKNOWN: 0xFF 228 | 229 | PowerStatus: 230 | ON: 0x00 231 | STANDBY: 0x01 232 | IN_TRANSITION_STANDBY_TO_ON: 0x02 233 | IN_TRANSITION_ON_TO_STANDBY: 0x03 234 | UNKNOWN: 0x99 235 | -------------------------------------------------------------------------------- /src/lib/emitLines.coffee: -------------------------------------------------------------------------------- 1 | # https://gist.github.com/TooTallNate/1785026 2 | 3 | # 4 | # A quick little thingy that takes a Stream instance and makes 5 | # it emit 'line' events when a newline is encountered. 6 | # * 7 | # Usage: 8 | # ‾‾‾‾‾ 9 | # emitLines(process.stdin) 10 | # process.stdin.resume() 11 | # process.stdin.setEncoding('utf8') 12 | # process.stdin.on('line', function (line) { 13 | # console.log(line event:', line) 14 | # }) 15 | # 16 | 17 | 18 | emitLines = ( stream ) -> 19 | backlog = '' 20 | 21 | stream.on 'data', (data) -> 22 | backlog += data 23 | 24 | n = backlog.indexOf('\n') 25 | 26 | # got a \n? emit one or more 'line' events 27 | while (~n) 28 | stream.emit( 'line', backlog.substring( 0, n ) ) 29 | backlog = backlog.substring( n + 1 ) 30 | n = backlog.indexOf( '\n' ) 31 | 32 | stream.on 'end', () -> 33 | stream.emit( 'line', backlog ) if backlog 34 | 35 | module.exports = emitLines 36 | -------------------------------------------------------------------------------- /src/node-cec.coffee: -------------------------------------------------------------------------------- 1 | { spawn } = require( 'child_process' ) 2 | { EventEmitter } = require( 'events' ) 3 | 4 | # ---------------------------------------------------------------------------- # 5 | # #NodeCEC 6 | # ---------------------------------------------------------------------------- # 7 | 8 | emitLines = require( './lib/emitLines' ) 9 | @CEC = require( './lib/cectypes' ) 10 | 11 | CEC = @CEC 12 | 13 | class @NodeCec extends EventEmitter 14 | 15 | constructor: ( @cecName=null ) -> 16 | @ready = false 17 | @stdinHandlers = [ 18 | 19 | { 20 | contains: 'waiting for input' 21 | callback: ( line ) => @emit( 'ready', @ ) 22 | } 23 | 24 | { 25 | match: /^TRAFFIC:/g 26 | callback: @processTraffic 27 | } 28 | 29 | ] 30 | 31 | start: ( @clientName = 'cec-client', @params... ) -> 32 | 33 | if @cecName? 34 | @params.push '-o' 35 | @params.push @cecName 36 | 37 | @client = spawn( @clientName, @params ) 38 | emitLines( @client.stdout ) 39 | 40 | @client.on( 'close', @onClose ) 41 | 42 | @client.stdout.on( 'line', (line) => 43 | 44 | @emit( 'data', line ) 45 | @processLine( line ) 46 | 47 | ) 48 | 49 | stop: () -> 50 | @emit( 'stop', @ ) 51 | @client.kill('SIGINT') 52 | 53 | onClose: () => 54 | @emit( 'stop', @ ) 55 | 56 | send: ( message ) -> 57 | @client.stdin.write( message + '\n' ) 58 | 59 | sendCommand: ( command... ) -> 60 | command = command.map( (hex) -> hex.toString(16) ) 61 | command = command.join( ':' ) 62 | @send( 'tx ' + command ) 63 | 64 | processLine: ( line ) -> 65 | @emit( 'line', line ) 66 | 67 | for handler in @stdinHandlers 68 | 69 | if handler.contains? 70 | if line.indexOf( handler.contains ) >= 0 71 | handler.callback( line ) 72 | 73 | else if handler.match? 74 | matches = line.match( handler.match ) 75 | if matches?.length > 0 76 | handler.callback( line ) 77 | 78 | else if handler.fn? 79 | if handler.fn( line ) 80 | handler.callback( line ) 81 | 82 | 83 | 84 | 85 | 86 | # -------------------------------------------------------------------------- # 87 | # #TRAFFIC 88 | # -------------------------------------------------------------------------- # 89 | 90 | processTraffic: ( traffic ) => 91 | packet = {} 92 | 93 | command = traffic.substr( traffic.indexOf(']\t') + 2 ) # "<< 0f:..:.." 94 | command = command.substr( command.indexOf( ' ' ) + 1 ) # "0f:..:.." 95 | 96 | tokens = command.split(':') # 0f .. .. 97 | 98 | if tokens? 99 | packet.tokens = tokens 100 | 101 | if tokens?.length > 0 102 | packet.source = tokens[0][0] 103 | packet.target = tokens[0][1] 104 | 105 | if tokens?.length > 1 106 | packet.opcode = parseInt( tokens[1], 16 ) 107 | packet.args = tokens[2..tokens.length].map( (hexString) -> 108 | parseInt( hexString, 16 ) 109 | ) 110 | 111 | @processPacket( packet ) 112 | 113 | 114 | processPacket: ( packet ) -> 115 | 116 | # no opcode? 117 | unless packet.tokens?.length > 1 118 | @emit( 'POLLING', packet ) 119 | return 120 | 121 | switch packet.opcode 122 | 123 | # ---------------------------------------------------------------------- # 124 | # #OSD 125 | 126 | when CEC.Opcode.SET_OSD_NAME 127 | break unless packet.args.length >= 1 128 | osdname = String.fromCharCode.apply( null, packet.args ) 129 | @emit( 'SET_OSD_NAME', packet, osdname ) 130 | return true 131 | 132 | 133 | 134 | # ---------------------------------------------------------------------- # 135 | # #SOURCE / ADDRESS 136 | 137 | when CEC.Opcode.ROUTING_CHANGE # SOURCE CHANGED 138 | break unless packet.args.length >= 4 139 | from = packet.args[0] << 8 | packet.args[1] 140 | to = packet.args[2] << 8 | packet.args[3] 141 | @emit( 'ROUTING_CHANGE', packet, from, to ) 142 | return true 143 | 144 | when CEC.Opcode.ACTIVE_SOURCE 145 | break unless packet.args.length >= 2 146 | source = packet.args[0] << 8 | packet.args[1] 147 | @emit( 'ACTIVE_SOURCE', packet, source ) 148 | return true 149 | 150 | when CEC.Opcode.REPORT_PHYSICAL_ADDRESS 151 | break unless packet.args.length >= 2 152 | source = packet.args[0] << 8 | packet.args[1] 153 | @emit( 'REPORT_PHYSICAL_ADDRESS', packet, source, packet.args[2] ) 154 | return true 155 | 156 | 157 | 158 | # ---------------------------------------------------------------------- # 159 | # #OTHER 160 | 161 | else 162 | 163 | opcodes = CEC.Opcode 164 | for key, opcode of opcodes when opcode == packet.opcode 165 | @emit( key, packet, packet.args... ) if key?.length > 0 166 | return true 167 | 168 | 169 | 170 | # emit unhandled packet 171 | @emit( 'packet', packet ) 172 | 173 | # not handled 174 | return false 175 | --------------------------------------------------------------------------------