UNPKG

homebridge-denon-tv

Version:

Homebridge plugin to control Denon/Marantz AV Receivers.

502 lines (439 loc) • 29.7 kB
import { promises as fsPromises } from 'fs'; import axios from 'axios'; import { Agent } from 'https'; import EventEmitter from 'events'; import ImpulseGenerator from './impulsegenerator.js'; import { XMLParser, XMLBuilder, XMLValidator } from 'fast-xml-parser'; import { ApiUrls, InputConversion, SoundModeConversion, BodyXml, PictureModesDenonNumber, InputMode } from './constants.js'; const INPUTS_CONVERSION_KEYS = Object.keys(InputConversion); const SOUND_MODES_CONVERSION_KEYS = Object.keys(SoundModeConversion); class Denon extends EventEmitter { constructor(config) { super(); const host = config.host; const port = config.port; this.host = host; this.generation = config.generation; this.zone = config.zone; this.inputs = config.inputs; this.devInfoFile = config.devInfoFile; this.inputsFile = config.inputsFile; this.getInputsFromDevice = this.zone < 3 ? config.getInputsFromDevice : false; this.getFavoritesFromDevice = this.getInputsFromDevice ? config.getFavoritesFromDevice : false; this.getQuickSmartSelectFromDevice = this.getInputsFromDevice ? config.getQuickSmartSelectFromDevice : false; this.enableDebugLog = config.enableDebugLog; this.deviceInfoUrl = [ApiUrls.DeviceInfoGen0, ApiUrls.DeviceInfoGen1, ApiUrls.DeviceInfoGen2][this.generation]; this.zoneStateUrl = [ApiUrls.MainZoneStatusLite, ApiUrls.Zone2StatusLite, ApiUrls.Zone3StatusLite, ApiUrls.SoundModeStatus, ApiUrls.MainZoneStatusLite][this.zone]; const baseUrl = `http://${host}:${port}`; this.axiosInstance = this.generation === 2 ? axios.create({ method: 'GET', baseURL: baseUrl, timeout: 15000, maxContentLength: 100000000, maxBodyLength: 1000000000, httpsAgent: new Agent({ rejectUnauthorized: false }) }) : axios.create({ method: 'GET', baseURL: baseUrl, timeout: 15000, maxContentLength: 100000000, maxBodyLength: 1000000000, }); this.axiosInstancePost = this.generation === 2 ? axios.create({ method: 'POST', baseURL: baseUrl, timeout: 15000, httpsAgent: new Agent({ rejectUnauthorized: false }) }) : axios.create({ method: 'POST', baseURL: baseUrl, timeout: 15000 }); const options = { ignoreAttributes: false, ignorePiTags: true, allowBooleanAttributes: true }; this.parseString = new XMLParser(options); this.power = false; this.reference = ''; this.volume = -80; this.volumeDisplay = false; this.mute = false; this.pictureMode = 0; this.soundMode = ''; this.audysseyMode = ''; this.devInfo = {}; this.object = {}; this.impulseGenerator = new ImpulseGenerator(); this.impulseGenerator.on('checkState', async () => { try { await this.checkState(); } catch (error) { const logError = config.disableLogError ? false : this.emit('error', `Impulse generator error: ${error}`); }; }).on('state', (state) => { const emitState = state ? this.emit('success', `Impulse generator started`) : this.emit('warn', `Impulse generator stopped`); }); }; async connect() { try { //get device info const deviceInfo = await this.axiosInstance(this.deviceInfoUrl); const parseData = this.parseString.parse(deviceInfo.data); const devInfo = [parseData.item, parseData.Device_Info, parseData.Device_Info][this.generation]; const debug = this.enableDebugLog ? this.emit('debug', `Connect data: ${JSON.stringify(devInfo, null, 2)}`) : false; this.devInfo = devInfo; //device info const deviceInfoKeys = Object.keys(devInfo); const deviceInfoVers = deviceInfoKeys.includes('DeviceInfoVers') ? devInfo.DeviceInfoVers : 0; const apiVersion = deviceInfoKeys.includes('CommApiVers') ? devInfo.CommApiVers : '000'; const gen = deviceInfoKeys.includes('Gen') ? devInfo.Gen : 0; const brandCode = deviceInfoKeys.includes('BrandCode') ? devInfo.BrandCode : 2; const manufacturer = ['Denon', 'Marantz', 'Denon/Marantz'][brandCode] ?? 'Denon/Marantz'; const productCategory = deviceInfoKeys.includes('ProductCategory') ? devInfo.ProductCategory : '00'; const categoryName = deviceInfoKeys.includes('CategoryName') ? devInfo.ProductCategory : 'Unknown'; const manualModelName = deviceInfoKeys.includes('ManualModelName') ? devInfo.ManualModelName : 'Unknown'; const deliveryCode = deviceInfoKeys.includes('DeliveryCode') ? devInfo.DeliveryCode : 0; const modelName = deviceInfoKeys.includes('ModelName') ? devInfo.ModelName : deviceInfoKeys.includes('FriendlyName') ? devInfo.FriendlyName.value : 'AV Receiver'; const macAddressSupported = deviceInfoKeys.includes('MacAddress'); const serialNumber = [macAddressSupported ? devInfo.MacAddress.toString() : `1234567654321${this.host}`, macAddressSupported ? devInfo.MacAddress.toString() : false, macAddressSupported ? devInfo.MacAddress.toString() : false][this.generation]; const firmwareRevision = deviceInfoKeys.includes('UpgradeVersion') ? devInfo.UpgradeVersion.toString() : '00'; const reloadDeviceInfo = deviceInfoKeys.includes('ReloadDeviceInfo') ? devInfo.ReloadDeviceInfo : 0; const deviceZones = deviceInfoKeys.includes('DeviceZones') ? devInfo.DeviceZones : 1; //device capabilities const capabilitiesSupport = deviceInfoKeys.includes('DeviceCapabilities'); const capabilities = capabilitiesSupport ? devInfo.DeviceCapabilities : this.object; const capabilitiesKeys = capabilitiesSupport ? Object.keys(capabilities) : []; //menu const capabilitiesMenuSupport = capabilitiesSupport && capabilitiesKeys.includes('Menu'); const capabilitiesMenu = capabilitiesMenuSupport ? devInfo.DeviceCapabilities.Menu : this.object; const capabilitiesMenuKeys = capabilitiesMenuSupport ? Object.keys(capabilitiesMenu) : []; //setup const capabilitiesSetupSupport = capabilitiesSupport && capabilitiesKeys.includes('Setup'); const capabilitiesSetup = capabilitiesSetupSupport ? devInfo.DeviceCapabilities.Setup : this.object; const capabilitiesSetupKeys = capabilitiesSetupSupport ? Object.keys(capabilitiesSetup) : []; const supportPartyMode = capabilitiesSetupSupport && capabilitiesSetupKeys.includes('PartyMode') ? capabilitiesSetup.PartyMode.Control === 1 : false; const supportTone = capabilitiesSetupSupport && capabilitiesSetupKeys.includes('ToneControl') ? capabilitiesSetup.ToneControl.Control === 1 : false; const supportSubwooferLevel = capabilitiesSetupSupport && capabilitiesSetupKeys.includes('SubwooferLevel') ? capabilitiesSetup.SubwooferLevel.Control === 1 : false; const supportChannelLevel = capabilitiesSetupSupport && capabilitiesSetupKeys.includes('ChannelLevel') ? capabilitiesSetup.ChannelLevel.Control === 1 : false; const supportAllZoneStereo = capabilitiesSetupSupport && capabilitiesSetupKeys.includes('AllZoneStereo') ? capabilitiesSetup.AllZoneStereo.Control === 1 : false; const supportPictureMode = capabilitiesSetupSupport && capabilitiesSetupKeys.includes('PictureMode') ? capabilitiesSetup.PictureMode.Control === 1 : false; const supportSoundMode = capabilitiesSetupSupport && capabilitiesSetupKeys.includes('SoundMode') ? capabilitiesSetup.SoundMode.Control === 1 : false this.supportPictureMode = supportPictureMode; this.supportSoundMode = supportSoundMode; //net link const capabilitiesNetLinkSupport = capabilitiesSupport && capabilitiesKeys.includes('NetLink'); const capabilitiesNetLink = capabilitiesNetLinkSupport ? devInfo.DeviceCapabilities.NetLink : this.object; const capabilitiesNetLinkKeys = capabilitiesNetLinkSupport ? Object.keys(capabilitiesNetLink) : []; //operation const capabilitiesOperationSupport = capabilitiesSupport && capabilitiesKeys.includes('Operation'); const capabilitiesOperation = capabilitiesOperationSupport ? devInfo.DeviceCapabilities.Operation : this.object; const capabilitiesOperationKeys = capabilitiesOperationSupport ? Object.keys(capabilitiesOperation) : []; const supportClock = capabilitiesOperationSupport & capabilitiesOperationKeys.includes('Clock') ? capabilitiesOperation.Clock.Control === 1 : false; const supportAllZonePower = capabilitiesOperationSupport & capabilitiesOperationKeys.includes('AllZonePower') ? capabilitiesOperation.AllZonePower.Control === 1 : false; const supportAllZoneMute = capabilitiesOperationSupport & capabilitiesOperationKeys.includes('AllZoneMute') ? capabilitiesOperation.AllZoneMute.Control === 1 : false; const supportFavorites = capabilitiesOperationSupport & capabilitiesOperationKeys.includes('Favorites') ? capabilitiesOperation.Favorites.Control === 1 : false; const supportFavoriteStation = capabilitiesOperationSupport & capabilitiesOperationKeys.includes('FavoriteStation') ? capabilitiesOperation.FavoriteStation.Control === 1 : false; //zone capabilities const zoneCapabilitiesSupport = this.zone < 3 ? deviceInfoKeys.includes('DeviceZoneCapabilities') : false; const zoneCapabilities = zoneCapabilitiesSupport ? Array.isArray(devInfo.DeviceZoneCapabilities) ? devInfo.DeviceZoneCapabilities[this.zone] : [devInfo.DeviceZoneCapabilities][0] : []; const zoneCapabilitiesKeys = zoneCapabilitiesSupport ? Object.keys(zoneCapabilities) : []; //zone const supportShortcut = zoneCapabilitiesKeys.includes('ShortcutControl') ? zoneCapabilities.ShortcutControl.Control === 1 : false; const supportPower = zoneCapabilitiesKeys.includes('Power') ? zoneCapabilities.Power.Control === 1 : false; const supportVolume = zoneCapabilitiesKeys.includes('Volume') ? zoneCapabilities.Volume.Control === 1 : false; const supportMute = zoneCapabilitiesKeys.includes('Mute') ? zoneCapabilities.Mute.Control === 1 : false; const supportInputSource = zoneCapabilitiesKeys.includes('InputSource') ? zoneCapabilities.InputSource.Control === 1 : false; //surround mode Marantz M-CR611 const supportSurroundMode = zoneCapabilitiesKeys.includes('SurroundMode') ? zoneCapabilities.SurroundMode.Control === 1 : false; const supportiPodPlayer = zoneCapabilitiesKeys.includes('iPodPlayer') ? zoneCapabilities.iPodPlayer.Control === 1 : false; //setup const zoneCapabilitiesSetupSupport = zoneCapabilitiesSupport && zoneCapabilitiesKeys.includes('Setup'); const zoneCapabilitiesSetup = zoneCapabilitiesSetupSupport ? zoneCapabilities.Setup : this.object; const zonesCapabilitiesSetupKeys = zoneCapabilitiesSetupSupport ? Object.keys(zoneCapabilitiesSetup) : []; const supportRestorer = zonesCapabilitiesSetupKeys.includes('Restorer') ? zoneCapabilitiesSetup.Restorer.Control === 1 : false; const supportToneControl = zonesCapabilitiesSetupKeys.includes('ToneControl') ? zoneCapabilitiesSetup.ToneControl.Control === 1 : false; //operation const zoneCapabilitiesOperationSupport = zoneCapabilitiesSupport && zoneCapabilitiesKeys.includes('Operation'); const zoneCapabilitiesOperation = zoneCapabilitiesOperationSupport ? zoneCapabilities.Operation : this.object; const zoneCapabilitiesOperationKeys = zoneCapabilitiesOperationSupport ? Object.keys(zoneCapabilitiesOperation) : []; const supportCursor = zoneCapabilitiesOperationKeys.includes('Cursor') ? zoneCapabilitiesOperation.Cursor.Control === 1 : false; const supportQuickSmartSelect = zoneCapabilitiesOperationKeys.includes('QuickSelect') ? zoneCapabilitiesOperation.QuickSelect.Control === 1 : false; const supportTunerOperation = zoneCapabilitiesOperationKeys.includes('TunerOperation') ? zoneCapabilitiesOperation.TunerOperation.Control === 1 : false; const supportBdOperation = zoneCapabilitiesOperationKeys.includes('BdOperation') ? zoneCapabilitiesOperation.BdOperation.Control === 1 : false; const supportCdOperation = zoneCapabilitiesOperationKeys.includes('CdOperation') ? zoneCapabilitiesOperation.CdOperation.Control === 1 : false; const supportExtCdOperation = zoneCapabilitiesOperationKeys.includes('ExtCdOperation') ? zoneCapabilitiesOperation.ExtCdOperation.Control === 1 : false; const supportBuildInCdOperation = zoneCapabilitiesOperationKeys.includes('BuildInCdOperation') ? zoneCapabilitiesOperation.BuildInCdOperation.Control === 1 : false; const supportPartyZone = zoneCapabilitiesOperationKeys.includes('PartyZone') ? zoneCapabilitiesOperation.PartyZone.Capability === 1 : false; //net usb Marantz M-CR611 const zoneCapabilitiesNetUsbSupport = zoneCapabilitiesSupport && zoneCapabilitiesKeys.includes('NetUsb'); const zoneCapabilitiesNetUsb = zoneCapabilitiesNetUsbSupport ? zoneCapabilities.NetUsb : this.object; const zoneCapabilitiesNetUsbKeys = zoneCapabilitiesNetUsbSupport ? Object.keys(zoneCapabilitiesNetUsb) : []; const supportInternetRadio = zoneCapabilitiesNetUsbKeys.includes('InternetRadio') ? zoneCapabilitiesNetUsb.InternetRadio.Control === 1 : false; const supportMediaServer = zoneCapabilitiesNetUsbKeys.includes('MediaServer') ? zoneCapabilitiesNetUsb.MediaServer.Control === 1 : false; const supportiPod = zoneCapabilitiesNetUsbKeys.includes('iPod') ? zoneCapabilitiesNetUsb.iPod.Control === 1 : false; const supportUsb = zoneCapabilitiesNetUsbKeys.includes('USB') ? zoneCapabilitiesNetUsb.USB.Control === 1 : false; const supportUsb2 = zoneCapabilitiesNetUsbKeys.includes('USB2') ? zoneCapabilitiesNetUsb.USB2.Control === 1 : false; const supportSpotifyConnect = zoneCapabilitiesNetUsbKeys.includes('SpotifyConnect') ? zoneCapabilitiesNetUsb.SpotifyConnect.Control === 1 : false; //check seriaql number if (!serialNumber) { this.emit('error', `Missing Serial Number`); return false; } //save device info to the file const saveDevInfo = this.zone === 0 ? await this.saveData(this.devInfoFile, devInfo) : false; //prepare all inputs const inputFuncListExistOldAvr = deviceInfoKeys.includes('InputFuncList'); const deviceInputsOldAvr = this.generation === 0 && inputFuncListExistOldAvr ? devInfo.InputFuncList.value : []; const deviceInputsNewAvr = supportInputSource ? zoneCapabilities.InputSource.List.Source : []; const deviceInputs = this.getInputsFromDevice ? [deviceInputsOldAvr, deviceInputsNewAvr, deviceInputsNewAvr][this.generation] : this.inputs; let allInputs = await this.getInputs(devInfo, this.generation, this.zone, deviceInputs, zoneCapabilities, this.getInputsFromDevice, this.getFavoritesFromDevice, this.getQuickSmartSelectFromDevice, supportFavorites, supportShortcut, supportQuickSmartSelect); const inputsExist = allInputs.length > 0; //check inputs if (!inputsExist) { this.emit('warn', `Found: 0 inputs, adding 1 default input`); let inputMode = 'SI'; switch (this.zone) { case 0: inputMode = 'SI'; break; case 1: inputMode = 'Z2'; break; case 2: inputMode = 'Z3'; break; case 3: inputMode = 'MS'; break; case 4: inputMode = 'SI'; break; } allInputs = [ { name: "CBL/SAT", reference: "SAT/CBL", mode: inputMode } ] } //save inputs if exist await this.saveData(this.inputsFile, allInputs); //connect to deice success this.emit('success', `Connect Success`) //emit device info this.emit('deviceInfo', manufacturer, modelName, serialNumber, firmwareRevision, deviceZones, apiVersion, supportPictureMode); return true; } catch (error) { throw new Error(`Connect error: ${error}`); }; }; async checkState() { try { //get zones status const deviceState = await this.axiosInstance(this.zoneStateUrl); const parseDeviceState = this.parseString.parse(deviceState.data); const devState = parseDeviceState.item; const debug = this.enableDebugLog ? this.emit('debug', `State: ${JSON.stringify(devState, null, 2)}`) : false; //get receiver status const statusKeys = Object.keys(devState); const power = devState.Power.value === 'ON'; const input = INPUTS_CONVERSION_KEYS.includes(devState.InputFuncSelect.value) ? InputConversion[devState.InputFuncSelect.value] : devState.InputFuncSelect.value; const volumeDisplay = statusKeys.includes('VolumeDisplay') ? devState.VolumeDisplay.value : false; const volume = devState.MasterVolume.value >= -79.5 ? devState.MasterVolume.value : -80; const mute = devState.Mute.value === 'on'; //get picture mode const checkPictureMode = this.supportPictureMode && power && this.zone === 0; const devicePictureMode = checkPictureMode ? await this.axiosInstancePost(ApiUrls.AppCommand, BodyXml.GetPictureMode) : false; const parseDevicePictureMode = checkPictureMode ? this.parseString.parse(devicePictureMode.data) : false; const debug1 = this.enableDebugLog && checkPictureMode ? this.emit('debug', `Picture mode: ${JSON.stringify(parseDevicePictureMode, null, 2)}`) : false; const pictureMode = checkPictureMode ? parseDevicePictureMode.rx.cmd.value : this.pictureMode; //get sound mode const checkZone = this.zone === 0 || this.zone === 3; const checkSoundeMode = this.supportSoundMode && power && checkZone; const deviceSoundMode = checkSoundeMode ? await this.axiosInstancePost(ApiUrls.AppCommand, BodyXml.GetSurroundModeStatus) : false; const parseDeviceSoundMode = checkSoundeMode ? this.parseString.parse(deviceSoundMode.data) : false; const debug2 = this.enableDebugLog && checkSoundeMode ? this.emit('debug', `Sound mode: ${JSON.stringify(parseDeviceSoundMode, null, 2)}`) : false; const soundMode = checkSoundeMode ? SOUND_MODES_CONVERSION_KEYS.includes((parseDeviceSoundMode.rx.cmd.surround).replace(/[^a-zA-Z0-9]/g, '').toUpperCase()) ? SoundModeConversion[(parseDeviceSoundMode.rx.cmd.surround).replace(/[^a-zA-Z0-9]/g, '').toUpperCase()] : (parseDeviceSoundMode.rx.cmd.surround).replace(/[^a-zA-Z0-9]/g, '').toUpperCase() : this.soundMode; //get audyssey mode const checkAudysseyMode = false //power && zone === 0; const deviceAudysseyMode = checkAudysseyMode ? await this.axiosInstancePost(ApiUrls.AppCommand, BodyXml.GetAudyssey) : false; const parseDeviceAudysseyMode = checkAudysseyMode ? this.parseString.parse(deviceAudysseyMode.data) : false; const debug3 = this.enableDebugLog && checkAudysseyMode ? this.emit('debug', `Audyssey mode: ${JSON.stringify(parseDeviceAudysseyMode, null, 2)}`) : false; const audysseyMode = checkAudysseyMode ? parseDeviceAudysseyMode.rx.cmd.value : this.audysseyMode; //select reference const reference = [input, input, input, soundMode, input][this.zone]; //restFul const restFul = this.zone < 3 ? this.emit('restFul', 'info', this.devInfo) : false; const restFul1 = this.zone < 3 ? this.emit('restFul', 'state', devState) : false; const restFul2 = this.zone < 3 && checkPictureMode ? this.emit('restFul', 'picture', { 'Picture Mode': PictureModesDenonNumber[pictureMode] }) : false; const restFul3 = this.zone < 3 && checkSoundeMode ? this.emit('restFul', 'surround', { 'Sound Mode': soundMode }) : false; //mqtt const mqtt = this.zone < 3 ? this.emit('mqtt', 'Info', this.devInfo) : false; const mqtt1 = this.zone < 3 ? this.emit('mqtt', 'State', devState) : false; const mqtt2 = this.zone < 3 && checkPictureMode ? this.emit('mqtt', 'Picture', { 'Picture Mode': PictureModesDenonNumber[pictureMode] }) : false; const mqtt3 = this.zone < 3 && checkSoundeMode ? this.emit('mqtt', 'Surround', { 'Sound Mode': soundMode }) : false; //update only if value change if (power === this.power && reference === this.reference && volume === this.volume && volumeDisplay === this.volumeDisplay && mute === this.mute && pictureMode === this.pictureMode && soundMode === this.soundMode) { return; }; this.power = power; this.reference = reference; this.volume = volume; this.volumeDisplay = volumeDisplay; this.mute = mute; this.pictureMode = pictureMode; this.soundMode = soundMode; this.audysseyMode = audysseyMode; //emit state changed this.emit('stateChanged', power, reference, volume, volumeDisplay, mute, pictureMode); return true; } catch (error) { throw new Error(`Check state error: ${error}`); }; }; async getInputs(devInfo, generation, zone, inputs, zoneCapabilities, getInputsFromDevice, getFavoritesFromDevice, getQuickSmartSelectFromDevice, supportFavorites, supportShortcut, supportQuickSmartSelect) { try { //temp array of inputs const tempInputs = []; //add inputs from avr or config let i = 0; for (const input of inputs) { const inputNameOldAvr = getInputsFromDevice && generation === 0 ? devInfo.RenameSource.value[i].trim() !== '' ? devInfo.RenameSource.value[i] : inputs[i] : `Input ${i}`; const inputName = getInputsFromDevice ? [inputNameOldAvr, input.DefaultName, input.DefaultName][generation] : input.name ?? false; const inputReference = getInputsFromDevice ? [input, input.FuncName, input.FuncName][generation] : input.reference ?? false; i++; //check input name and reference if (!inputName || !inputReference) { continue; } const obj = { 'name': inputName, 'reference': inputReference } tempInputs.push(obj); }; //add schortcuts const deviceSchortcuts = getInputsFromDevice && supportShortcut && Array.isArray(zoneCapabilities.ShortcutControl.EntryList.Shortcut) ? zoneCapabilities.ShortcutControl.EntryList.Shortcut : []; for (const shortcut of deviceSchortcuts) { const category = shortcut.Category; //1, 2, 3 Quick/Smart Select, 4 Inputs, 5 Sound Mode const shortcutName = shortcut.DispName ?? false; const shortcutReference = shortcut.FuncName ?? false; //check schorcut category, name and reference if (category !== '4' || !shortcutName || !shortcutReference) { continue; } const obj = { 'name': shortcutName, 'reference': shortcutReference } tempInputs.push(obj); }; //add favorites const deviceFavorites = getFavoritesFromDevice && supportFavorites && Array.isArray(devInfo.DeviceCapabilities.Operation.Favorites) ? devInfo.DeviceCapabilities.Operation.Favorites : []; for (const favorite of deviceFavorites) { const favoriteName = favorite.DispName ?? false; const favoriteReference = favorite.FuncName ?? false; //check favorite name and reference if (!favoriteName || !favoriteReference) { continue; } const obj = { 'name': favoriteName, 'reference': favoriteReference } tempInputs.push(obj); }; //add quick and smart select const deviceQuickSmartSelect = getQuickSmartSelectFromDevice && supportQuickSmartSelect ? zoneCapabilities.Operation.QuickSelect : {}; const quickSelectCount = getQuickSmartSelectFromDevice && supportQuickSmartSelect ? deviceQuickSmartSelect.MaxQuickSelect : 0; for (let j = 0; j < quickSelectCount; j++) { const quickSelect = deviceQuickSmartSelect[`QuickSelect${j + 1}`]; const quickSelectName = quickSelect.Name ?? false; const quickSelectReference = quickSelect.FuncName ?? false; //check quick select name and reference if (!quickSelectName || !quickSelectReference) { continue; } const obj = { 'name': quickSelectName, 'reference': quickSelectReference } tempInputs.push(obj); }; //chack duplicated inputs and convert reference const allInputs = []; for (const input of tempInputs) { const inputName = input.name; let inputReference = INPUTS_CONVERSION_KEYS.includes(input.reference) ? InputConversion[input.reference] : input.reference; let inputMode = 'SI'; //zones switch (zone) { case 0: //Denon const inputReferenceSubstring = inputReference.substring(0, 5) ?? 'Unknown'; const inputModeExist = inputReferenceSubstring in InputMode; //Marantz M-CR611 const inputReferenceSubstring1 = inputReference.substring(0, 2) ?? 'Unknown'; const inputModeExist1 = inputReferenceSubstring1 in InputMode; inputReference = inputModeExist1 ? inputReference.substring(3) : inputReference; inputMode = inputModeExist ? InputMode[inputReferenceSubstring] : inputModeExist1 ? InputMode[inputReferenceSubstring1] : inputMode; break; case 1: inputMode = 'Z2'; break; case 2: inputMode = 'Z3'; break; case 3: inputMode = 'MS'; break; case 4: inputMode = 'SI'; break; } const duplicatedInput = allInputs.some(input => input.reference === inputReference); if (!inputName || !inputReference || !inputMode || duplicatedInput) { continue; } const obj = { 'name': inputName, 'reference': inputReference, 'mode': inputMode } allInputs.push(obj); } const debug = this.enableDebugLog ? this.emit('info', `All Inputs: ${JSON.stringify(allInputs, null, 2)}`) : false; return allInputs; } catch (error) { throw new Error(`Get inputus error: ${error}`); } }; async saveData(path, data) { try { data = JSON.stringify(data, null, 2); await fsPromises.writeFile(path, data); const debug = this.enableDebugLog ? this.emit('debug', `Saved data: ${data}`) : false; return true; } catch (error) { throw new Error(`Save data error: ${error}`); }; }; async send(command) { try { const path = `${ApiUrls.iPhoneDirect}${command}`; await this.axiosInstance(path); const debug = this.enableDebugLog ? this.emit('debug', `Send path: ${path}`) : false; return true; } catch (error) { throw new Error(`Send data error: ${error}`); }; }; }; export default Denon;