UNPKG

homebridge-pioneer-avr

Version:

Pioneer AVR plugin for homebridge: https://github.com/nfarina/homebridge

337 lines (294 loc) 10.4 kB
/* Helper module for controlling Pioneer AVR */ const request = require('request'); const TelnetAvr = require('./telnet-avr'); // Reference fot input id -> Characteristic.InputSourceType const inputToType = { '00': 0, // PHONO -> Characteristic.InputSourceType.OTHER '01': 0, // CD -> Characteristic.InputSourceType.OTHER '02': 2, // TUNER -> Characteristic.InputSourceType.TUNER '03': 0, // TAPE -> Characteristic.InputSourceType.OTHER '04': 0, // DVD -> Characteristic.InputSourceType.OTHER '05': 3, // TV -> Characteristic.InputSourceType.HDMI '06': 3, // CBL/SAT -> Characteristic.InputSourceType.HDMI '10': 4, // VIDEO -> Characteristic.InputSourceType.COMPOSITE_VIDEO '12': 0, // MULTI CH IN -> Characteristic.InputSourceType.OTHER '13': 0, // USB-DAC -> Characteristic.InputSourceType.OTHER '14': 6, // VIDEOS2 -> Characteristic.InputSourceType.COMPONENT_VIDEO '15': 3, // DVR/BDR -> Characteristic.InputSourceType.HDMI '17': 9, // USB/iPod -> Characteristic.InputSourceType.USB '18': 2, // XM RADIO -> Characteristic.InputSourceType.TUNER '19': 3, // HDMI1 -> Characteristic.InputSourceType.HDMI '20': 3, // HDMI2 -> Characteristic.InputSourceType.HDMI '21': 3, // HDMI3 -> Characteristic.InputSourceType.HDMI '22': 3, // HDMI4 -> Characteristic.InputSourceType.HDMI '23': 3, // HDMI5 -> Characteristic.InputSourceType.HDMI '24': 3, // HDMI6 -> Characteristic.InputSourceType.HDMI '25': 3, // BD -> Characteristic.InputSourceType.HDMI '26': 10, // MEDIA GALLERY -> Characteristic.InputSourceType.APPLICATION '27': 0, // SIRIUS -> Characteristic.InputSourceType.OTHER '31': 3, // HDMI CYCLE -> Characteristic.InputSourceType.HDMI '33': 0, // ADAPTER -> Characteristic.InputSourceType.OTHER '34': 3, // HDMI7-> Characteristic.InputSourceType.HDMI '35': 3, // HDMI8-> Characteristic.InputSourceType.HDMI '38': 2, // NETRADIO -> Characteristic.InputSourceType.TUNER '40': 0, // SIRIUS -> Characteristic.InputSourceType.OTHER '41': 0, // PANDORA -> Characteristic.InputSourceType.OTHER '44': 0, // MEDIA SERVER -> Characteristic.InputSourceType.OTHER '45': 0, // FAVORITE -> Characteristic.InputSourceType.OTHER '48': 0, // MHL -> Characteristic.InputSourceType.OTHER '49': 0, // GAME -> Characteristic.InputSourceType.OTHER '57': 0 // SPOTIFY -> Characteristic.InputSourceType.OTHER }; function PioneerAvr(log, host, port) { const me = this; this.log = log; this.host = host; this.port = port; // Current AV status this.state = { volume: null, on: null, muted: null, input: null }; // Inputs' list this.inputs = []; // Web interface ? this.web = false; this.webStatusUrl = 'http://' + this.host + '/StatusHandler.asp'; this.webEventHandlerBaseUrl = 'http://' + this.host + '/EventHandler.asp?WebToHostItem='; request .get(this.webStatusUrl) .on('response', function(response) { if (response.statusCode == '200') { me.log.info('Web Interface enabled'); this.web = true; } }); // Communication Initialization this.s = new TelnetAvr(this.host, this.port); // Dealing with input's initialization this.initCount = 0; this.isReady = false; } module.exports = PioneerAvr; PioneerAvr.prototype.loadInputs = function(callback) { // Queue and send all inputs discovery commands this.log.debug('Discovering inputs'); for (var key in inputToType) { this.log.debug('Trying Input key: %s', key); this.sendCommand(`?RGB${key}`, callback); } }; // Power methods PioneerAvr.prototype.__updatePower = function(callback) { this.sendCommand('?P', callback); }; PioneerAvr.prototype.powerStatus = function(callback) { require('deasync').sleep(100); this.__updatePower(() => { callback(null, this.state.on); }); }; PioneerAvr.prototype.powerOn = function() { this.log.debug('Power on'); if (this.web) { request.get(this.webEventHandlerBaseUrl + 'PO'); } else { this.sendCommand('PO'); } }; PioneerAvr.prototype.powerOff = function() { this.log.debug('Power off'); if (this.web) { request.get(this.webEventHandlerBaseUrl + 'PF'); } else { this.sendCommand('PF'); } }; // Volume methods PioneerAvr.prototype.__updateVolume = function(callback) { this.sendCommand('?V', callback); }; PioneerAvr.prototype.volumeStatus = function(callback) { this.__updateVolume(() => { callback(null, this.state.volume); }); }; PioneerAvr.prototype.setVolume = function(targetVolume, callback) { var vsxVol = targetVolume * 185 / 100; vsxVol = Math.floor(vsxVol); var pad = "000"; var vsxVolStr = pad.substring(0, pad.length - vsxVol.toString().length) + vsxVol.toString(); this.sendCommand(`${vsxVolStr}VL\r\n`); callback(); }; PioneerAvr.prototype.volumeUp = function() { this.log.debug('Volume up'); if (this.web) { request.get(this.webEventHandlerBaseUrl + 'VU'); } else { this.sendCommand('VU'); } }; PioneerAvr.prototype.volumeDown = function() { this.log.debug('Volume down'); if (this.web) { request.get(this.webEventHandlerBaseUrl + 'VD'); } else { this.sendCommand('VD'); } }; // Mute methods PioneerAvr.prototype.__updateMute = function(callback) { this.sendCommand('?M', callback); }; PioneerAvr.prototype.muteStatus = function(callback) { this.__updateMute(() => { callback(null, this.state.muted); }); }; PioneerAvr.prototype.muteOn = function() { this.log.debug('Mute on'); if (this.web) { request.get(this.webEventHandlerBaseUrl + 'MO'); } else { this.sendCommand('MO'); } }; PioneerAvr.prototype.muteOff = function() { this.log.debug('Mute off'); if (this.web) { request.get(this.webEventHandlerBaseUrl + 'MF'); } else { this.sendCommand('MF'); } }; // Input management method PioneerAvr.prototype.__updateInput = function(callback) { this.sendCommand('?F', callback); }; PioneerAvr.prototype.inputStatus = function(callback) { this.__updateInput(() => { callback(null, this.state.input); }); }; PioneerAvr.prototype.setInput = function(id) { if (this.web) { request.get(this.webEventHandlerBaseUrl + `${id}FN`); } else { this.sendCommand(`${id}FN`); } }; PioneerAvr.prototype.renameInput = function (id, newName) { let shrinkName = newName.substring(0,14); this.sendCommand(`${shrinkName}1RGB${id}`); }; // Remote Key methods PioneerAvr.prototype.remoteKey = function (rk) { // Implemented key from CURSOR OPERATION switch (rk) { case 'UP': this.sendCommand('CUP'); break; case 'DOWN': this.sendCommand('CDN'); break; case 'LEFT': this.sendCommand('CLE'); break; case 'RIGHT': this.sendCommand('CRI'); break; case 'ENTER': this.sendCommand('CEN'); break; case 'RETURN': this.sendCommand('CRT'); break; case 'HOME_MENU': this.sendCommand('HM'); break; default: this.log.info('Unhandled remote key : %s', rk); } }; // Send command and process return PioneerAvr.prototype.sendCommand = async function(command, callback) { // Main method to send a command to AVR try { this.log.debug('Send command : %s', command); data = await this.s.sendMessage(command); this.log.debug('Receive data : %s', data); } catch (e) { this.log.error(e) } // Data returned for power status if (data.startsWith('PWR')) { this.log.debug('Receive Power status : %s', data); this.state.on = parseInt(data[3], 10) === 0; callback(); } // Data returned for mute status if (data.startsWith('MUT')) { this.log.debug('Receive Mute status : %s', data); this.state.muted = parseInt(data[3], 10) === 0; callback(); } // Data returned for volume status if (data.startsWith('VOL')) { var vol = data.substring(3); var volPctF = Math.floor(parseInt(vol) * 100 / 185); this.state.volume = Math.floor(volPctF); this.log.debug("Volume is %s (%s%)", vol, this.state.volume); callback(); } // Data returned for input status if (data.startsWith('FN')) { this.log.debug('Receive Input status : %s', data); let inputId = data.substr(2); let inputIndex = null; for (var x in this.inputs) { if (this.inputs[x].id == inputId) { inputIndex = x; } } this.state.input = inputIndex; callback(); } // Data returned for input queries if (data.startsWith('RGB')) { let tmpInput = { id: data.substr(3,2), name: data.substr(6).trim(), type: inputToType[data.substr(3,2)] }; this.inputs.push(tmpInput); if (!this.isReady) { this.initCount = this.initCount + 1; this.log.debug('Input [%s] discovered (id: %s, type: %s). InitCount=%s/%s', tmpInput.name, tmpInput.id, tmpInput.type, this.initCount, Object.keys(inputToType).length ); if (this.initCount == Object.keys(inputToType).length) this.isReady = true; } callback(this.inputs.length-1); } // E06 is returned when input not exists if (data.startsWith('E06')) { this.log.debug('Receive E06 error'); if (!this.isReady) { this.initCount = this.initCount + 1; this.log.debug('Input does not exists. InitCount=%s/%s', this.initCount, Object.keys(inputToType).length ); if (this.initCount == Object.keys(inputToType).length) this.isReady = true; } } };