UNPKG

homebridge-pioneer-avr-2025

Version:

A Pioneer AVR plugin for homebridge. This plugin is designed for Pioneer models that use Telnet Commands (e.g., VSX-922). It may not be compatible with newer models (e.g., VSX-LX304).

468 lines 21 kB
"use strict"; // src/pioneer-avr/volume.ts var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.VolumeManagementMixin = VolumeManagementMixin; const exitHandler_1 = require("../exitHandler"); /** * This mixin adds volume management methods to a base class, including volume control, * mute functionality, and listening mode toggling. * @param Base - The base class to extend with volume management methods. * @returns A new class that extends the base class with added volume management functionality. */ function VolumeManagementMixin(Base) { return class extends Base { // public allListeingModes: { [key: string]: string } = { // '0001': 'STEREO (cyclic)', // '0009': 'STEREO (direct set) (set to SCI-FI mode.)', // '0151': 'Auto Level Control (A.L.C.)', // '0003': 'Front Stage Surround Advance Focus', // '0004': 'Front Stage Surround Advance Wide (set to PURE DIRECT)', // '0153': 'RETRIEVER AIR', // '0010': 'STANDARD mode.', // '0011': '(2ch source)', // '0013': 'PRO LOGIC2 MOVIE', // '0018': 'PRO LOGIC2x MOVIE', // '0014': 'PRO LOGIC2 MUSIC', // '0019': 'PRO LOGIC2x MUSIC', // '0015': 'PRO LOGIC2 GAME', // '0020': 'PRO LOGIC2x GAME', // '0032': 'WIDE SURROUND MOVIE', // '0033': 'WIDE SURROUND MUSIC', // '0012': 'PRO LOGIC', // '0016': 'Neo:6 CINEMA', // '0017': 'Neo:6 MUSIC', // '0028': 'XM HD SURROUND', // '0029': 'NEURAL SURROUND', // '0024': '(Multi ch source)+PRO LOGIC2x MUSIC', // '0034': '(Multi-ch Source)+PRO LOGIC2z HEIGHT', // '0035': '(Multi-ch Source)+WIDE SURROUND MOVIE', // '0036': '(Multi-ch Source)+WIDE SURROUND MUSIC', // '0025': 'DTS-ES Neo:6', // '0026': 'DTS-ES matrix', // '0027': 'DTS-ES discrete', // '0101': 'ACTION', // '0103': 'DRAMA', // '0102': 'SCI-FI', // '0105': 'MONO FILM', // '0104': 'ENTERTAINMENT SHOW', // '0106': 'EXPANDED THEATER', // '0116': 'TV SURROUND', // '0118': 'ADVANCED GAME', // '0117': 'SPORTS', // '0107': 'CLASSICAL', // '0110': 'ROCK/POP', // '0109': 'UNPLUGGED', // '0112': 'EXTENDED STEREO', // '0113': 'PHONES SURROUND', // '0051': 'PROLOGIC + THX CINEMA', // '0052': 'PL2 MOVIE + THX CINEMA', // '0053': 'Neo:6 CINEMA + THX CINEMA', // '0054': 'PL2x MOVIE + THX CINEMA', // '0092': 'PL2z HEIGHT + THX CINEMA', // '0055': 'THX SELECT2 GAMES', // '0093': 'PL2z HEIGHT + THX MUSIC', // '0073': 'Neo:6 MUSIC + THX MUSIC', // '0074': 'PL2 GAME + THX GAMES', // '0075': 'PL2x GAME + THX GAMES', // '0094': 'PL2z HEIGHT + THX GAMES', // '0076': 'THX ULTRA2 GAMES', // '0077': 'PROLOGIC + THX MUSIC', // '0057': 'THX SURROUND EX (for multi ch)', // '0058': 'PL2x MOVIE + THX CINEMA (for multi ch)', // '0095': 'PL2z HEIGHT + THX CINEMA (for multi ch)', // '0067': 'ES 8ch DISCRETE + THX CINEMA (for multi ch)', // '0031': 'PRO LOGIC2z Height', // '0100': 'ADVANCED SURROUND (cyclic)', // '0050': 'THX (cyclic)', // '0068': 'THX CINEMA (for 2ch)', // '0069': 'THX MUSIC (for 2ch)', // '0070': 'THX GAMES (for 2ch)', // '0071': 'PL2 MUSIC + THX MUSIC', // '0072': 'PL2x MUSIC + THX MUSIC', // '0078': 'PROLOGIC + THX GAMES', // '0056': 'THX CINEMA (for multi ch)', // '0059': 'ES Neo:6 + THX CINEMA (for multi ch)', // '0060': 'ES MATRIX + THX CINEMA (for multi ch)', // '0061': 'ES DISCRETE + THX CINEMA (for multi ch)', // '0062': 'THX SELECT2 CINEMA (for multi ch)', // '0063': 'THX SELECT2 MUSIC (for multi ch)', // '0064': 'THX SELECT2 GAMES (for multi ch)', // '0065': 'THX ULTRA2 CINEMA (for multi ch)', // '0066': 'THX ULTRA2 MUSIC (for multi ch)', // '0079': 'THX ULTRA2 GAMES (for multi ch)', // '0080': 'THX MUSIC (for multi ch)', // '0081': 'THX GAMES (for multi ch)', // '0082': 'PL2x MUSIC + THX MUSIC (for multi ch)', // '0096': 'PL2z HEIGHT + THX MUSIC (for multi ch)', // '0083': 'EX + THX GAMES (for multi ch)', // '0097': 'PL2z HEIGHT + THX GAMES (for multi ch)', // '0084': 'Neo:6 + THX MUSIC (for multi ch)', // '0085': 'Neo:6 + THX GAMES (for multi ch)', // '0086': 'ES MATRIX + THX MUSIC (for multi ch)', // '0087': 'ES MATRIX + THX GAMES (for multi ch)', // '0088': 'ES DISCRETE + THX MUSIC (for multi ch)', // '0089': 'ES DISCRETE + THX GAMES (for multi ch)', // '0090': 'ES 8CH DISCRETE + THX MUSIC (for multi ch)', // '0091': 'ES 8CH DISCRETE + THX GAMES (for multi ch)', // '0005': 'AUTO SURR/STREAM DIRECT (cyclic)', // '0006': 'AUTO SURROUND', // '0152': 'OPTIMUM SURROUND', // '0007': 'DIRECT', // '0008': 'PURE DIRECT' // }; constructor(...args) { super(...args); this.updateVolumeTimeout = null; this.cancelVolumeDownSteps = false; this.activeVolumeDownStepTimeouts = []; // Array to track active timeouts this.lastSetVolume = ''; // Handle disconnection by resetting the volume display this.telnetAvr.addOnDisconnectCallback(() => { var _a, _b; try { (_a = this.functionSetLightbulbVolume) === null || _a === void 0 ? void 0 : _a.call(this, this.state.volume); } catch (e) { this.log.debug('functionSetLightbulbVolume error', e); } try { (_b = this.functionSetSwitchListeningMode) === null || _b === void 0 ? void 0 : _b.call(this); } catch (e) { this.log.debug('functionSetSwitchListeningMode error', e); } }); // Handle reconnection by updating the volume, mute, and listening mode this.telnetAvr.addOnConnectCallback(() => __awaiter(this, void 0, void 0, function* () { setTimeout(() => __awaiter(this, void 0, void 0, function* () { var _a, _b; try { let error = null; let attempts = 0; if (this.state.on) { do { error = yield new Promise((resolve) => { this.__updateListeningMode((err) => resolve(err)); }); if (error) { yield new Promise((resolve) => setTimeout(resolve, 1500)); attempts++; } } while (error && attempts < 10); } error = null; attempts = 0; do { error = yield new Promise((resolve) => { this.__updateVolume((err) => resolve(err)); }); if (error) { yield new Promise((resolve) => setTimeout(resolve, 1500)); attempts++; } } while (error && attempts < 10); error = null; attempts = 0; do { error = yield new Promise((resolve) => { this.__updateMute((err) => resolve(err)); }); if (error) { yield new Promise((resolve) => setTimeout(resolve, 1500)); attempts++; } } while (error && attempts < 10); (_a = this.functionSetLightbulbVolume) === null || _a === void 0 ? void 0 : _a.call(this, this.state.volume); (_b = this.functionSetSwitchListeningMode) === null || _b === void 0 ? void 0 : _b.call(this); } catch (e) { this.log.debug('functionSetLightbulbVolume error', e); } }), 5321); })); // Handle exit by resetting the volume display before shutdown (0, exitHandler_1.addExitHandler)(() => { var _a, _b; this.state.on = false; try { (_a = this.functionSetLightbulbVolume) === null || _a === void 0 ? void 0 : _a.call(this, this.state.volume); } catch (e) { this.log.debug('functionSetLightbulbVolume error', e); } try { (_b = this.functionSetSwitchListeningMode) === null || _b === void 0 ? void 0 : _b.call(this); } catch (e) { this.log.debug('functionSetSwitchListeningMode error', e); } }, this); } /** * Placeholder for setting the volume of the AVR. * Intended to be overridden externally. */ functionSetLightbulbVolume(_argument) { // Implement volume setting logic here } functionSetLightbulbMuted(_argument) { } functionSetSwitchListeningMode(_argument) { } /** * Sends a volume status query to the AVR and updates the volume state. * @param callback - Function to run after updating the volume. */ __updateVolume(callback) { return __awaiter(this, void 0, void 0, function* () { this.telnetAvr.sendMessage('?V', 'VOL', callback); }); } /** * Sends a mute status query to the AVR and updates the mute state. * @param callback - Function to run after updating the mute state. */ __updateMute(callback) { return __awaiter(this, void 0, void 0, function* () { this.telnetAvr.sendMessage('?M', 'MUT', callback); }); } /** * Retrieves the current volume status. * @param callback - The function to handle the volume result. */ volumeStatus(callback) { if (this.state.volume !== null) { callback(null, this.state.volume); return; } this.__updateVolume(() => { callback(null, this.state.volume); }); } /** * Sets a specific volume on the AVR. * @param targetVolume - The desired volume level. * @param callback - The function to call after setting the volume. */ setVolume(targetVolume, callback) { if (!this.telnetAvr || !this.telnetAvr.connectionReady || !this.state.on) { return; } targetVolume = parseInt(targetVolume.toString(), 10); if (isNaN(targetVolume) || Math.floor(targetVolume) === this.state.volume) { if (callback) { try { callback(null, ''); } catch (e) { this.log.debug('', e); } } return; } let vsxVol = 0; if (this.maxVolume > 0) { const minVolumeIn185 = this.minVolume / 100 * 185; const maxVolumeIn185 = this.maxVolume / 100 * 185; vsxVol = targetVolume / 100 * (maxVolumeIn185 - minVolumeIn185) + minVolumeIn185; } else { vsxVol = targetVolume * 185 / 100; } vsxVol = Math.floor(vsxVol); const vsxVolStr = vsxVol.toString().padStart(3, '0'); if (this.lastSetVolume !== vsxVolStr) { this.lastSetVolume = vsxVolStr; this.telnetAvr.sendMessage(`${vsxVolStr}VL`, undefined, callback); this.lastUserInteraction = Date.now(); } } /** * Increases the volume by one step. */ volumeUp() { this.lastUserInteraction = Date.now(); if (!this.telnetAvr || !this.telnetAvr.connectionReady || !this.state.on) { return; } this.log.debug('Volume up'); if (this.updateVolumeTimeout) { clearTimeout(this.updateVolumeTimeout); } this.telnetAvr.sendMessage('VU', undefined, () => { this.updateVolumeTimeout = setTimeout(() => { this.__updateVolume(() => { }); this.__updateMute(() => { }); }, 1000); }); } /** * Decreases the volume by three steps, with a delay of 100ms between each step. */ volumeDown() { this.lastUserInteraction = Date.now(); if (!this.telnetAvr || !this.telnetAvr.connectionReady || !this.state.on) { return; } this.log.debug('Volume down'); // Cancel any ongoing steps and clear active timeouts this.cancelVolumeDownSteps = true; if (this.updateVolumeTimeout) { clearTimeout(this.updateVolumeTimeout); } this.activeVolumeDownStepTimeouts.forEach((timeout) => { clearTimeout(timeout); }); this.activeVolumeDownStepTimeouts = []; const steps = 3; const delay = 25; // Function to execute each step with a delay const executeStep = (step) => { if (step > 0 && !this.cancelVolumeDownSteps) { this.telnetAvr.sendMessage('VD', 'VOL', () => { const timeout = setTimeout(() => { executeStep(step - 1); }, delay); this.activeVolumeDownStepTimeouts.push(timeout); // Track the timeout }); } else if (step === 0 && !this.cancelVolumeDownSteps) { if (this.updateVolumeTimeout) { clearTimeout(this.updateVolumeTimeout); } // After the last step, update volume and mute status this.updateVolumeTimeout = setTimeout(() => { this.__updateVolume(() => { }); this.__updateMute(() => { }); }, 1000); } }; // Reset the cancel flag and start the first step this.cancelVolumeDownSteps = false; executeStep(steps); } /** * Retrieves the mute status of the AVR. * @param callback - The function to handle the mute result. */ muteStatus(callback) { if (this.state.muted !== null) { callback(null, this.state.muted); return; } this.__updateMute(() => { callback(null, this.state.muted); }); } /** * Sends a mute-on command to the AVR. */ muteOn() { this.lastUserInteraction = Date.now(); if (!this.telnetAvr || !this.telnetAvr.connectionReady || !this.state.on || this.state.muted === true) { return; } this.log.debug('Mute on'); this.telnetAvr.sendMessage('MO'); } /** * Sends a mute-off command to the AVR. */ muteOff() { this.lastUserInteraction = Date.now(); if (!this.telnetAvr || !this.telnetAvr.connectionReady || !this.state.on || this.state.muted === false) { return; } this.log.debug('Mute off'); this.telnetAvr.sendMessage('MF'); } /** * Sends a query to update the current listening mode. * @param callback - The function to handle the response. */ __updateListeningMode(callback) { this.telnetAvr.sendMessage('?S', 'SR', callback); } // /** // * Retrieves the current listening mode. // * @param callback - The function to handle the listening mode result. // */ // public getListeningMode(callback: (err: any, mode?: string) => void) { // if (this.state.listeningMode) { // callback(null, this.state.listeningMode); // // } else { // this.__updateListeningMode(() => { // callback(null, this.state.listeningMode ?? undefined); // }); // } // } /** * Toggles the listening mode between presets. * @param callback - Optional function to handle the toggle result. */ toggleListeningMode(callback) { var _a; this.lastUserInteraction = Date.now(); // Check if the AVR is ready and if a listening mode is set if (!this.isReady || !this.state.listeningMode) { if (callback) { callback(null, (_a = this.state.listeningMode) !== null && _a !== void 0 ? _a : ''); } return; } this.log.debug('Toggle Listening Mode', this.state.listeningMode); const isValidListeningMode = (value, defaultValue) => { return /^[0-9]{4}$/.test(value || '') ? value : defaultValue; }; const listeningModeOne = isValidListeningMode(this.device.listeningMode || this.platform.config.listeningMode, '0013'); // PRO LOGIC2 MOVIE const listeningModeFallback = isValidListeningMode(this.device.listeningModeFallback || this.platform.config.listeningModeFallback, '0101'); // ACTION const listeningModeOther = isValidListeningMode(this.device.listeningModeOther || this.platform.config.listeningModeOther, '0112'); // EXTENDED STEREO if ([listeningModeOne, listeningModeFallback].includes(this.state.listeningMode)) { this.telnetAvr.sendMessage(listeningModeOther + 'SR'); this.state.listeningMode = listeningModeOther; if (callback) { callback(null, this.state.listeningMode); } } else { this.state.listeningMode = listeningModeOne; this.telnetAvr.sendMessage('!' + listeningModeOne + 'SR', 'SR', (error, _data) => { if (error) { this.state.listeningMode = listeningModeFallback; setTimeout(() => { this.telnetAvr.sendMessage(listeningModeFallback + 'SR'); }, 100); } if (callback) { callback(null, this.state.listeningMode); } }); } } }; } //# sourceMappingURL=volume.js.map