From a050023d943e707d45f1c53c38cc7a447f0acbb9 Mon Sep 17 00:00:00 2001 From: Grzegorz Date: Wed, 14 Aug 2024 12:42:26 +0200 Subject: [PATCH] cleanup --- package.json | 2 +- src/denon.js | 242 +++++------ src/mainzone.js | 1096 +++++++++++++++++++++++------------------------ src/surround.js | 932 ++++++++++++++++++++-------------------- src/zone2.js | 976 +++++++++++++++++++++-------------------- src/zone3.js | 974 +++++++++++++++++++++-------------------- 6 files changed, 2092 insertions(+), 2130 deletions(-) diff --git a/package.json b/package.json index a561f3c..ee7e88e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "displayName": "Denon TV", "name": "homebridge-denon-tv", - "version": "4.14.1", + "version": "4.14.2", "description": "Homebridge plugin to control Denon/Marantz AV Receivers.", "license": "MIT", "author": "grzegorz914", diff --git a/src/denon.js b/src/denon.js index a897514..ddba8aa 100644 --- a/src/denon.js +++ b/src/denon.js @@ -322,142 +322,136 @@ class DENON extends EventEmitter { impulseGenerator.start(); }; - saveDevInfo(path, devInfo) { - return new Promise(async (resolve, reject) => { - try { - const info = JSON.stringify(devInfo, null, 2); - await fsPromises.writeFile(path, info); - const debug = !this.debugLog ? false : this.emit('message', `saved device info: ${info}`); - - resolve(); - } catch (error) { - reject(error); - }; - }); + async saveDevInfo(path, devInfo) { + try { + const info = JSON.stringify(devInfo, null, 2); + await fsPromises.writeFile(path, info); + const debug = !this.debugLog ? false : this.emit('message', `saved device info: ${info}`); + + return true; + } catch (error) { + this.emitDeviceInfo('error', error); + }; }; - saveInputs(path, devInfo, generation, zone, zoneInputSurroundName, inputs, zoneCapabilities, getInputsFromDevice, getFavoritesFromDevice, getQuickSmartSelectFromDevice, supportFavorites, supportShortcut, supportQuickSmartSelect) { - return new Promise(async (resolve, reject) => { - try { - //inputs - const tempInputs = []; - const inputsArr = []; - 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; - const inputReference = getInputsFromDevice ? [input, input.FuncName, input.FuncName][generation] : input.reference; - const obj = { - 'name': inputName, - 'reference': inputReference - } - tempInputs.push(obj); - i++; - }; + async saveInputs(path, devInfo, generation, zone, zoneInputSurroundName, inputs, zoneCapabilities, getInputsFromDevice, getFavoritesFromDevice, getQuickSmartSelectFromDevice, supportFavorites, supportShortcut, supportQuickSmartSelect) { + try { + //inputs + const tempInputs = []; + const inputsArr = []; + 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; + const inputReference = getInputsFromDevice ? [input, input.FuncName, input.FuncName][generation] : input.reference; + const obj = { + 'name': inputName, + 'reference': inputReference + } + tempInputs.push(obj); + i++; + }; - //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; - const shortcutReference = shortcut.FuncName; - const obj = { - 'name': shortcutName, - 'reference': shortcutReference - } - const push = category === '4' ? tempInputs.push(obj) : false; - }; + //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; + const shortcutReference = shortcut.FuncName; + const obj = { + 'name': shortcutName, + 'reference': shortcutReference + } + const push = category === '4' ? tempInputs.push(obj) : false; + }; - //favorites - const deviceFavorites = getFavoritesFromDevice && supportFavorites && Array.isArray(devInfo.DeviceCapabilities.Operation.Favorites) ? devInfo.DeviceCapabilities.Operation.Favorites : []; - for (const favorite of deviceFavorites) { - const favoriteName = favorite.DispName; - const favoriteReference = favorite.FuncName; - const obj = { - 'name': favoriteName, - 'reference': favoriteReference - } - tempInputs.push(obj); - }; + //favorites + const deviceFavorites = getFavoritesFromDevice && supportFavorites && Array.isArray(devInfo.DeviceCapabilities.Operation.Favorites) ? devInfo.DeviceCapabilities.Operation.Favorites : []; + for (const favorite of deviceFavorites) { + const favoriteName = favorite.DispName; + const favoriteReference = favorite.FuncName; + const obj = { + 'name': favoriteName, + 'reference': favoriteReference + } + tempInputs.push(obj); + }; - //quick and smart select - const deviceQuickSmartSelect = getQuickSmartSelectFromDevice && supportQuickSmartSelect ? zoneCapabilities.Operation.QuickSelect : {}; - const quickSelectCount = getQuickSmartSelectFromDevice && supportQuickSmartSelect ? deviceQuickSmartSelect.MaxQuickSelect : 0; - for (let j = 1; j < quickSelectCount; j++) { - const quickSelect = deviceQuickSmartSelect[`QuickSelect${j}`]; - const quickSelectName = quickSelect.Name; - const quickSelectReference = quickSelect.FuncName; - const obj = { - 'name': quickSelectName, - 'reference': quickSelectReference - } - tempInputs.push(obj); - }; + //quick and smart select + const deviceQuickSmartSelect = getQuickSmartSelectFromDevice && supportQuickSmartSelect ? zoneCapabilities.Operation.QuickSelect : {}; + const quickSelectCount = getQuickSmartSelectFromDevice && supportQuickSmartSelect ? deviceQuickSmartSelect.MaxQuickSelect : 0; + for (let j = 1; j < quickSelectCount; j++) { + const quickSelect = deviceQuickSmartSelect[`QuickSelect${j}`]; + const quickSelectName = quickSelect.Name; + const quickSelectReference = quickSelect.FuncName; + const obj = { + 'name': quickSelectName, + 'reference': quickSelectReference + } + tempInputs.push(obj); + }; - //chack duplicated inputs and convert reference - const debug = !this.debugLog ? false : this.emit('message', `temp Inputs: ${JSON.stringify(tempInputs, null, 2)}`); - for (const input of tempInputs) { - const inputName = input.name; - let inputReference = INPUTS_CONVERSION_KEYS.includes(input.reference) ? CONSTANTS.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 CONSTANTS.InputMode; - - //Marantz M-CR611 - const inputReferenceSubstring1 = inputReference.substring(0, 2) ?? 'Unknown'; - const inputModeExist1 = inputReferenceSubstring1 in CONSTANTS.InputMode; - - inputReference = inputModeExist1 ? inputReference.substring(3) : inputReference; - inputMode = inputModeExist ? CONSTANTS.InputMode[inputReferenceSubstring] : inputModeExist1 ? CONSTANTS.InputMode[inputReferenceSubstring1] : inputMode; - break; - case 1: - inputMode = 'Z2'; - break; - case 2: - inputMode = 'Z3'; - break; - case 3: - inputMode = 'MS'; - break; - } - - const obj = { - 'name': inputName, - 'reference': inputReference, - 'mode': inputMode - } - - const duplicatedInput = inputsArr.some(input => input.reference === inputReference); - const push = inputName && inputReference && inputMode && !duplicatedInput ? inputsArr.push(obj) : false; + //chack duplicated inputs and convert reference + const debug = !this.debugLog ? false : this.emit('message', `temp Inputs: ${JSON.stringify(tempInputs, null, 2)}`); + for (const input of tempInputs) { + const inputName = input.name; + let inputReference = INPUTS_CONVERSION_KEYS.includes(input.reference) ? CONSTANTS.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 CONSTANTS.InputMode; + + //Marantz M-CR611 + const inputReferenceSubstring1 = inputReference.substring(0, 2) ?? 'Unknown'; + const inputModeExist1 = inputReferenceSubstring1 in CONSTANTS.InputMode; + + inputReference = inputModeExist1 ? inputReference.substring(3) : inputReference; + inputMode = inputModeExist ? CONSTANTS.InputMode[inputReferenceSubstring] : inputModeExist1 ? CONSTANTS.InputMode[inputReferenceSubstring1] : inputMode; + break; + case 1: + inputMode = 'Z2'; + break; + case 2: + inputMode = 'Z3'; + break; + case 3: + inputMode = 'MS'; + break; } - //save inputs - const allInputs = JSON.stringify(inputsArr, null, 2); - await fsPromises.writeFile(path, allInputs); - const debug1 = !this.debugLog ? false : this.emit('message', `saved ${zoneInputSurroundName}: ${allInputs}`); + const obj = { + 'name': inputName, + 'reference': inputReference, + 'mode': inputMode + } - resolve(inputsArr) - } catch (error) { - reject(error); + const duplicatedInput = inputsArr.some(input => input.reference === inputReference); + const push = inputName && inputReference && inputMode && !duplicatedInput ? inputsArr.push(obj) : false; } - }); + + //save inputs + const allInputs = JSON.stringify(inputsArr, null, 2); + await fsPromises.writeFile(path, allInputs); + const debug1 = !this.debugLog ? false : this.emit('message', `saved ${zoneInputSurroundName}: ${allInputs}`); + + return inputsArr; + } catch (error) { + this.emitDeviceInfo('error', error); + } }; - send(command) { - return new Promise(async (resolve, reject) => { - try { - const path = `${CONSTANTS.ApiUrls.iPhoneDirect}${command}`; - await this.axiosInstance(path); - resolve(); - } catch (error) { - reject(error); - }; - }); + async send(command) { + try { + const path = `${CONSTANTS.ApiUrls.iPhoneDirect}${command}`; + await this.axiosInstance(path); + return true; + } catch (error) { + this.emitDeviceInfo('error', error); + }; }; }; module.exports = DENON; diff --git a/src/mainzone.js b/src/mainzone.js index 7b7a7f6..46edb32 100644 --- a/src/mainzone.js +++ b/src/mainzone.js @@ -369,642 +369,634 @@ class MainZone extends EventEmitter { }); }; - displayOrder() { - return new Promise((resolve, reject) => { - try { - switch (this.inputsDisplayOrder) { - case 0: - this.inputsConfigured.sort((a, b) => a.identifier - b.identifier); - break; - case 1: - this.inputsConfigured.sort((a, b) => a.name.localeCompare(b.name)); - break; - case 2: - this.inputsConfigured.sort((a, b) => b.name.localeCompare(a.name)); - break; - case 3: - this.inputsConfigured.sort((a, b) => a.reference.localeCompare(b.reference)); - break; - case 4: - this.inputsConfigured.sort((a, b) => b.reference.localeCompare(a.reference)); - break; - } - const debug = !this.enableDebugMode ? false : this.emit('debug', `Inputs display order: ${JSON.stringify(this.inputsConfigured, null, 2)}`); - - const displayOrder = this.inputsConfigured.map(input => input.identifier); - this.televisionService.setCharacteristic(Characteristic.DisplayOrder, Encode(1, displayOrder).toString('base64')); - resolve(); - } catch (error) { - reject(error); - }; - }); + async displayOrder() { + try { + switch (this.inputsDisplayOrder) { + case 0: + this.inputsConfigured.sort((a, b) => a.identifier - b.identifier); + break; + case 1: + this.inputsConfigured.sort((a, b) => a.name.localeCompare(b.name)); + break; + case 2: + this.inputsConfigured.sort((a, b) => b.name.localeCompare(a.name)); + break; + case 3: + this.inputsConfigured.sort((a, b) => a.reference.localeCompare(b.reference)); + break; + case 4: + this.inputsConfigured.sort((a, b) => b.reference.localeCompare(a.reference)); + break; + } + const debug = !this.enableDebugMode ? false : this.emit('debug', `Inputs display order: ${JSON.stringify(this.inputsConfigured, null, 2)}`); + + const displayOrder = this.inputsConfigured.map(input => input.identifier); + this.televisionService.setCharacteristic(Characteristic.DisplayOrder, Encode(1, displayOrder).toString('base64')); + return true; + } catch (error) { + this.emitDeviceInfo('error', error); + }; } - saveData(path, data) { - return new Promise(async (resolve, reject) => { - try { - await fsPromises.writeFile(path, JSON.stringify(data, null, 2)); - const debug = !this.enableDebugMode ? false : this.emit('debug', `Saved data: ${JSON.stringify(data, null, 2)}`); - resolve(); - } catch (error) { - reject(error); - }; - }); + async saveData(path, data) { + try { + await fsPromises.writeFile(path, JSON.stringify(data, null, 2)); + const debug = !this.enableDebugMode ? false : this.emit('debug', `Saved data: ${JSON.stringify(data, null, 2)}`); + return true; + } catch (error) { + this.emitDeviceInfo('error', error); + }; } - readData(path) { - return new Promise(async (resolve, reject) => { - try { - const data = await fsPromises.readFile(path); - resolve(data); - } catch (error) { - reject(`Read saved data error: ${error}`); - }; - }); + async readData(path) { + try { + const data = await fsPromises.readFile(path); + return data; + } catch (error) { + this.emitDeviceInfo('error', `Read saved data error: ${error}`); + }; } //prepare accessory - prepareAccessory(allInputs) { - return new Promise((resolve, reject) => { - try { - //accessory - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare accessory`); - const accessoryName = this.name; - const accessoryUUID = AccessoryUUID.generate(this.serialNumber + this.zone); - const accessoryCategory = Categories.AUDIO_RECEIVER; - const accessory = new Accessory(accessoryName, accessoryUUID, accessoryCategory); - - //information service - const debug1 = !this.enableDebugMode ? false : this.emit('debug', `Prepare information service`); - this.informationService = accessory.getService(Service.AccessoryInformation) - .setCharacteristic(Characteristic.Manufacturer, this.manufacturer) - .setCharacteristic(Characteristic.Model, this.modelName) - .setCharacteristic(Characteristic.SerialNumber, this.serialNumber) - .setCharacteristic(Characteristic.FirmwareRevision, this.firmwareRevision); - this.allServices.push(this.informationService); - - - //prepare television service - const debug2 = !this.enableDebugMode ? false : this.emit('debug', `Prepare television service`); - this.televisionService = accessory.addService(Service.Television, `${accessoryName} Television`, 'Television'); - this.televisionService.setCharacteristic(Characteristic.ConfiguredName, accessoryName); - this.televisionService.setCharacteristic(Characteristic.SleepDiscoveryMode, 1); - - this.televisionService.getCharacteristic(Characteristic.Active) - .onGet(async () => { - const state = this.power; - return state; - }) - .onSet(async (state) => { - if (this.power == state) { - return; + async prepareAccessory(allInputs) { + try { + //accessory + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare accessory`); + const accessoryName = this.name; + const accessoryUUID = AccessoryUUID.generate(this.serialNumber + this.zone); + const accessoryCategory = Categories.AUDIO_RECEIVER; + const accessory = new Accessory(accessoryName, accessoryUUID, accessoryCategory); + + //information service + const debug1 = !this.enableDebugMode ? false : this.emit('debug', `Prepare information service`); + this.informationService = accessory.getService(Service.AccessoryInformation) + .setCharacteristic(Characteristic.Manufacturer, this.manufacturer) + .setCharacteristic(Characteristic.Model, this.modelName) + .setCharacteristic(Characteristic.SerialNumber, this.serialNumber) + .setCharacteristic(Characteristic.FirmwareRevision, this.firmwareRevision); + this.allServices.push(this.informationService); + + + //prepare television service + const debug2 = !this.enableDebugMode ? false : this.emit('debug', `Prepare television service`); + this.televisionService = accessory.addService(Service.Television, `${accessoryName} Television`, 'Television'); + this.televisionService.setCharacteristic(Characteristic.ConfiguredName, accessoryName); + this.televisionService.setCharacteristic(Characteristic.SleepDiscoveryMode, 1); + + this.televisionService.getCharacteristic(Characteristic.Active) + .onGet(async () => { + const state = this.power; + return state; + }) + .onSet(async (state) => { + if (this.power == state) { + return; + } + + try { + const powerState = this.masterPower ? (state ? 'PWON' : 'PWSTANDBY') : (state ? 'ZMON' : 'ZMOFF'); + await this.denon.send(powerState); + const info = this.disableLogInfo ? false : this.emit('message', `set Power: ${powerState}`); + } catch (error) { + this.emit('error', `set Power error: ${error}`); + }; + }); + + this.televisionService.getCharacteristic(Characteristic.ActiveIdentifier) + .onGet(async () => { + const inputIdentifier = this.inputIdentifier; + return inputIdentifier; + }) + .onSet(async (activeIdentifier) => { + try { + const index = this.inputsConfigured.findIndex(input => input.identifier === activeIdentifier); + const input = this.inputsConfigured[index]; + const inputName = input.name; + const inputMode = input.mode; + const inputReference = input.reference; + const reference = `${inputMode}${inputReference}`; + + switch (this.power) { + case false: + await new Promise(resolve => setTimeout(resolve, 4000)); + const tryAgain = this.power ? this.televisionService.setCharacteristic(Characteristic.ActiveIdentifier, activeIdentifier) : false; + break; + case true: + await this.denon.send(reference); + const info = this.disableLogInfo ? false : this.emit('message', `set Input Name: ${inputName}, Reference: ${reference}`); + break; + } + } catch (error) { + this.emit('error', `set Input error: ${error}`); + }; + }); + + this.televisionService.getCharacteristic(Characteristic.RemoteKey) + .onSet(async (command) => { + try { + const rcMedia = this.inputReference === 'SPOTIFY' || this.inputReference === 'BT' || this.inputReference === 'USB/IPOD' || this.inputReference === 'NET' || this.inputReference === 'MPLAY'; + switch (command) { + case Characteristic.RemoteKey.REWIND: + command = rcMedia ? 'NS9E' : 'MN9E'; + break; + case Characteristic.RemoteKey.FAST_FORWARD: + command = rcMedia ? 'NS9D' : 'MN9D'; + break; + case Characteristic.RemoteKey.NEXT_TRACK: + command = rcMedia ? 'MN9D' : 'MN9F'; + break; + case Characteristic.RemoteKey.PREVIOUS_TRACK: + command = rcMedia ? 'MN9E' : 'MN9G'; + break; + case Characteristic.RemoteKey.ARROW_UP: + command = rcMedia ? 'NS90' : 'MNCUP'; + break; + case Characteristic.RemoteKey.ARROW_DOWN: + command = rcMedia ? 'NS91' : 'MNCDN'; + break; + case Characteristic.RemoteKey.ARROW_LEFT: + command = rcMedia ? 'NS92' : 'MNCLT'; + break; + case Characteristic.RemoteKey.ARROW_RIGHT: + command = rcMedia ? 'NS93' : 'MNENT'; + break; + case Characteristic.RemoteKey.SELECT: + command = rcMedia ? 'NS94' : 'MNENT'; + break; + case Characteristic.RemoteKey.BACK: + command = rcMedia ? 'MNRTN' : 'MNRTN'; + break; + case Characteristic.RemoteKey.EXIT: + command = rcMedia ? 'MNRTN' : 'MNRTN'; + break; + case Characteristic.RemoteKey.PLAY_PAUSE: + command = rcMedia ? (this.mediaState ? 'NS9B' : 'NS9A') : 'NS94'; + this.mediaState = !this.mediaState; + break; + case Characteristic.RemoteKey.INFORMATION: + command = this.infoButtonCommand; + break; } - try { - const powerState = this.masterPower ? (state ? 'PWON' : 'PWSTANDBY') : (state ? 'ZMON' : 'ZMOFF'); - await this.denon.send(powerState); - const info = this.disableLogInfo ? false : this.emit('message', `set Power: ${powerState}`); - } catch (error) { - this.emit('error', `set Power error: ${error}`); - }; - }); + await this.denon.send(command); + const info = this.disableLogInfo ? false : this.emit('message', `set Remote Key: ${command}`); + } catch (error) { + this.emit('error', `set Remote Key error: ${error}`); + }; + }); + + + //optional television characteristics + this.televisionService.getCharacteristic(Characteristic.Brightness) + .onGet(async () => { + const brightness = this.brightness; + return brightness; + }) + .onSet(async (value) => { + try { + const newValue = (value / 100) * 12; + const brightness = `PVBR ${(newValue)}`; + await this.denon.send(brightness); + const info = this.disableLogInfo ? false : this.emit('message', `set Brightness: ${value}`); + } catch (error) { + this.emit('error', `set Brightness error: ${error}`); + }; + }); - this.televisionService.getCharacteristic(Characteristic.ActiveIdentifier) + if (this.supportPictureMode) { + this.televisionService.getCharacteristic(Characteristic.PictureMode) .onGet(async () => { - const inputIdentifier = this.inputIdentifier; - return inputIdentifier; + const pictureMode = this.pictureMode; + return pictureMode; }) - .onSet(async (activeIdentifier) => { - try { - const index = this.inputsConfigured.findIndex(input => input.identifier === activeIdentifier); - const input = this.inputsConfigured[index]; - const inputName = input.name; - const inputMode = input.mode; - const inputReference = input.reference; - const reference = `${inputMode}${inputReference}`; - - switch (this.power) { - case false: - await new Promise(resolve => setTimeout(resolve, 4000)); - const tryAgain = this.power ? this.televisionService.setCharacteristic(Characteristic.ActiveIdentifier, activeIdentifier) : false; - break; - case true: - await this.denon.send(reference); - const info = this.disableLogInfo ? false : this.emit('message', `set Input Name: ${inputName}, Reference: ${reference}`); - break; - } - } catch (error) { - this.emit('error', `set Input error: ${error}`); - }; - }); - - this.televisionService.getCharacteristic(Characteristic.RemoteKey) .onSet(async (command) => { try { - const rcMedia = this.inputReference === 'SPOTIFY' || this.inputReference === 'BT' || this.inputReference === 'USB/IPOD' || this.inputReference === 'NET' || this.inputReference === 'MPLAY'; switch (command) { - case Characteristic.RemoteKey.REWIND: - command = rcMedia ? 'NS9E' : 'MN9E'; + case Characteristic.PictureMode.OTHER: //0 off + command = 'PVOFF'; break; - case Characteristic.RemoteKey.FAST_FORWARD: - command = rcMedia ? 'NS9D' : 'MN9D'; + case Characteristic.PictureMode.STANDARD: //1 standard + command = 'PVSTD'; break; - case Characteristic.RemoteKey.NEXT_TRACK: - command = rcMedia ? 'MN9D' : 'MN9F'; + case Characteristic.PictureMode.CALIBRATED: //5 isf day + command = 'PVDAY'; break; - case Characteristic.RemoteKey.PREVIOUS_TRACK: - command = rcMedia ? 'MN9E' : 'MN9G'; + case Characteristic.PictureMode.CALIBRATED_DARK: //6 isf night + command = 'PVNGT'; break; - case Characteristic.RemoteKey.ARROW_UP: - command = rcMedia ? 'NS90' : 'MNCUP'; + case Characteristic.PictureMode.VIVID: //3 vivid + command = 'PVVVD'; break; - case Characteristic.RemoteKey.ARROW_DOWN: - command = rcMedia ? 'NS91' : 'MNCDN'; + case Characteristic.PictureMode.GAME: //4 streaming + command = 'PVSTM'; break; - case Characteristic.RemoteKey.ARROW_LEFT: - command = rcMedia ? 'NS92' : 'MNCLT'; + case Characteristic.PictureMode.COMPUTER: //2 movie + command = 'PVMOV'; break; - case Characteristic.RemoteKey.ARROW_RIGHT: - command = rcMedia ? 'NS93' : 'MNENT'; - break; - case Characteristic.RemoteKey.SELECT: - command = rcMedia ? 'NS94' : 'MNENT'; - break; - case Characteristic.RemoteKey.BACK: - command = rcMedia ? 'MNRTN' : 'MNRTN'; - break; - case Characteristic.RemoteKey.EXIT: - command = rcMedia ? 'MNRTN' : 'MNRTN'; - break; - case Characteristic.RemoteKey.PLAY_PAUSE: - command = rcMedia ? (this.mediaState ? 'NS9B' : 'NS9A') : 'NS94'; - this.mediaState = !this.mediaState; - break; - case Characteristic.RemoteKey.INFORMATION: - command = this.infoButtonCommand; + case Characteristic.PictureMode.CUSTOM: //7 custom + command = 'PVCTM'; break; } await this.denon.send(command); - const info = this.disableLogInfo ? false : this.emit('message', `set Remote Key: ${command}`); + const info = this.disableLogInfo ? false : this.emit('message', `set Picture Mode: ${CONSTANTS.PictureModesDenonString[command]}`); } catch (error) { - this.emit('error', `set Remote Key error: ${error}`); + this.emit('error', `set Picture Mode error: ${error}`); }; }); + }; + this.televisionService.getCharacteristic(Characteristic.PowerModeSelection) + .onSet(async (command) => { + try { + switch (command) { + case Characteristic.PowerModeSelection.SHOW: + command = 'MNOPT'; + break; + case Characteristic.PowerModeSelection.HIDE: + command = 'MNRTN'; + break; + } - //optional television characteristics - this.televisionService.getCharacteristic(Characteristic.Brightness) - .onGet(async () => { - const brightness = this.brightness; - return brightness; - }) - .onSet(async (value) => { - try { - const newValue = (value / 100) * 12; - const brightness = `PVBR ${(newValue)}`; - await this.denon.send(brightness); - const info = this.disableLogInfo ? false : this.emit('message', `set Brightness: ${value}`); - } catch (error) { - this.emit('error', `set Brightness error: ${error}`); - }; - }); + await this.denon.send(command); + const info = this.disableLogInfo ? false : this.emit('message', `set Power Mode Selection: ${command === 'MNOPT' ? 'SHOW' : 'HIDE'}`); + } catch (error) { + this.emit('error', `set Power Mode Selection error: ${error}`); + }; + }); + this.allServices.push(this.televisionService); + + //prepare speaker service + const debug3 = !this.enableDebugMode ? false : this.emit('debug', `Prepare speaker service`); + this.speakerService = accessory.addService(Service.TelevisionSpeaker, `${accessoryName} Speaker`, 'Speaker'); + this.speakerService.getCharacteristic(Characteristic.Active) + .onGet(async () => { + const state = this.power; + return state; + }) + .onSet(async (state) => { + }); + this.speakerService.getCharacteristic(Characteristic.VolumeControlType) + .onGet(async () => { + const controlType = this.volumeControlType === 'Relative' ? 1 : 3; //none, relative, relative with current, absolute + const state = 3; + return state; + }) + this.speakerService.getCharacteristic(Characteristic.VolumeSelector) + .onSet(async (command) => { + try { + switch (command) { + case Characteristic.VolumeSelector.INCREMENT: + command = 'MVUP'; + break; + case Characteristic.VolumeSelector.DECREMENT: + command = 'MVDOWN'; + break; + } - if (this.supportPictureMode) { - this.televisionService.getCharacteristic(Characteristic.PictureMode) - .onGet(async () => { - const pictureMode = this.pictureMode; - return pictureMode; - }) - .onSet(async (command) => { - try { - switch (command) { - case Characteristic.PictureMode.OTHER: //0 off - command = 'PVOFF'; - break; - case Characteristic.PictureMode.STANDARD: //1 standard - command = 'PVSTD'; - break; - case Characteristic.PictureMode.CALIBRATED: //5 isf day - command = 'PVDAY'; - break; - case Characteristic.PictureMode.CALIBRATED_DARK: //6 isf night - command = 'PVNGT'; - break; - case Characteristic.PictureMode.VIVID: //3 vivid - command = 'PVVVD'; - break; - case Characteristic.PictureMode.GAME: //4 streaming - command = 'PVSTM'; - break; - case Characteristic.PictureMode.COMPUTER: //2 movie - command = 'PVMOV'; - break; - case Characteristic.PictureMode.CUSTOM: //7 custom - command = 'PVCTM'; - break; - } + await this.denon.send(command); + const info = this.disableLogInfo ? false : this.emit('message', `set Volume Selector: ${command}`); + } catch (error) { + this.emit('error', `set Volume Selector error: ${error}`); + }; + }); + + this.speakerService.getCharacteristic(Characteristic.Volume) + .setProps({ + minValue: 0, + maxValue: this.volumeMax + }) + .onGet(async () => { + const volume = this.volume; + return volume; + }) + .onSet(async (value) => { + try { + value = value < 10 ? `0${value}` : value; + const volume = `MV${value}`; + await this.denon.send(volume); + const info = this.disableLogInfo ? false : this.emit('message', `set Volume: ${value - 80}`); + } catch (error) { + this.emit('error', `set Volume error: ${error}`); + }; + }); + + this.speakerService.getCharacteristic(Characteristic.Mute) + .onGet(async () => { + const state = this.mute; + return state; + }) + .onSet(async (state) => { + try { + const muteState = state ? 'MUON' : 'MUOFF'; + await this.denon.send(muteState); + const info = this.disableLogInfo ? false : this.emit('message', `set Mute: ${state ? 'ON' : 'OFF'}`); + } catch (error) { + this.emit('error', `set Mute error: ${error}`); + }; + }); - await this.denon.send(command); - const info = this.disableLogInfo ? false : this.emit('message', `set Picture Mode: ${CONSTANTS.PictureModesDenonString[command]}`); - } catch (error) { - this.emit('error', `set Picture Mode error: ${error}`); - }; - }); - }; + this.allServices.push(this.speakerService); - this.televisionService.getCharacteristic(Characteristic.PowerModeSelection) - .onSet(async (command) => { - try { - switch (command) { - case Characteristic.PowerModeSelection.SHOW: - command = 'MNOPT'; - break; - case Characteristic.PowerModeSelection.HIDE: - command = 'MNRTN'; - break; - } + //prepare inputs service + const debug8 = !this.enableDebugMode ? false : this.emit('debug', `Prepare inputs services`); - await this.denon.send(command); - const info = this.disableLogInfo ? false : this.emit('message', `set Power Mode Selection: ${command === 'MNOPT' ? 'SHOW' : 'HIDE'}`); - } catch (error) { - this.emit('error', `set Power Mode Selection error: ${error}`); - }; - }); - this.allServices.push(this.televisionService); + //check possible inputs count (max 85) + const inputs = allInputs; + const inputsCount = inputs.length; + const possibleInputsCount = 85 - this.allServices.length; + const maxInputsCount = inputsCount >= possibleInputsCount ? possibleInputsCount : inputsCount; + for (let i = 0; i < maxInputsCount; i++) { + //input + const input = inputs[i]; + const inputIdentifier = i + 1; - //prepare speaker service - const debug3 = !this.enableDebugMode ? false : this.emit('debug', `Prepare speaker service`); - this.speakerService = accessory.addService(Service.TelevisionSpeaker, `${accessoryName} Speaker`, 'Speaker'); - this.speakerService.getCharacteristic(Characteristic.Active) - .onGet(async () => { - const state = this.power; - return state; - }) - .onSet(async (state) => { - }); - this.speakerService.getCharacteristic(Characteristic.VolumeControlType) - .onGet(async () => { - const controlType = this.volumeControlType === 'Relative' ? 1 : 3; //none, relative, relative with current, absolute - const state = 3; - return state; - }) - this.speakerService.getCharacteristic(Characteristic.VolumeSelector) - .onSet(async (command) => { - try { - switch (command) { - case Characteristic.VolumeSelector.INCREMENT: - command = 'MVUP'; - break; - case Characteristic.VolumeSelector.DECREMENT: - command = 'MVDOWN'; - break; - } + //get input reference + const inputReference = input.reference; - await this.denon.send(command); - const info = this.disableLogInfo ? false : this.emit('message', `set Volume Selector: ${command}`); - } catch (error) { - this.emit('error', `set Volume Selector error: ${error}`); - }; - }); + //get input name + const savedInputsName = this.savedInputsNames[inputReference] ?? false; + input.name = savedInputsName ? savedInputsName : input.name; - this.speakerService.getCharacteristic(Characteristic.Volume) - .setProps({ - minValue: 0, - maxValue: this.volumeMax - }) + //get type + const inputSourceType = 0; + + //get configured + const isConfigured = 1; + + //get visibility + input.visibility = this.savedInputsTargetVisibility[inputReference] ?? 0; + + //add identifier to the input + input.identifier = inputIdentifier; + + //input service + const inputService = accessory.addService(Service.InputSource, input.name, `Input ${inputIdentifier}`); + inputService + .setCharacteristic(Characteristic.Identifier, inputIdentifier) + .setCharacteristic(Characteristic.Name, input.name) + .setCharacteristic(Characteristic.IsConfigured, isConfigured) + .setCharacteristic(Characteristic.InputSourceType, inputSourceType) + .setCharacteristic(Characteristic.CurrentVisibilityState, input.visibility) + + inputService.getCharacteristic(Characteristic.ConfiguredName) .onGet(async () => { - const volume = this.volume; - return volume; + return input.name; }) .onSet(async (value) => { + if (value === this.savedInputsNames[inputReference]) { + return; + } + try { - value = value < 10 ? `0${value}` : value; - const volume = `MV${value}`; - await this.denon.send(volume); - const info = this.disableLogInfo ? false : this.emit('message', `set Volume: ${value - 80}`); + input.name = value; + this.savedInputsNames[inputReference] = value; + await this.saveData(this.inputsNamesFile, this.savedInputsNames); + const debug = !this.enableDebugMode ? false : this.emit('debug', `Saved Input Name: ${value}, Reference: ${inputReference}`); + + //sort inputs + const index = this.inputsConfigured.findIndex(input => input.reference === inputReference); + this.inputsConfigured[index].name = value; + await this.displayOrder(); } catch (error) { - this.emit('error', `set Volume error: ${error}`); - }; + this.emit('error', `save Input Name error: ${error}`); + } }); - this.speakerService.getCharacteristic(Characteristic.Mute) + inputService.getCharacteristic(Characteristic.TargetVisibilityState) .onGet(async () => { - const state = this.mute; - return state; + return input.visibility; }) .onSet(async (state) => { + if (state === this.savedInputsTargetVisibility[inputReference]) { + return; + } + try { - const muteState = state ? 'MUON' : 'MUOFF'; - await this.denon.send(muteState); - const info = this.disableLogInfo ? false : this.emit('message', `set Mute: ${state ? 'ON' : 'OFF'}`); + input.visibility = state; + this.savedInputsTargetVisibility[inputReference] = state; + await this.saveData(this.inputsTargetVisibilityFile, this.savedInputsTargetVisibility); + const debug = !this.enableDebugMode ? false : this.emit('debug', `Saved Input: ${input.name} Target Visibility: ${state ? 'HIDEN' : 'SHOWN'}`); } catch (error) { - this.emit('error', `set Mute error: ${error}`); - }; + this.emit('error', `save Input Target Visibility error: ${error}`); + } }); - this.allServices.push(this.speakerService); - - //prepare inputs service - const debug8 = !this.enableDebugMode ? false : this.emit('debug', `Prepare inputs services`); - - //check possible inputs count (max 85) - const inputs = allInputs; - const inputsCount = inputs.length; - const possibleInputsCount = 85 - this.allServices.length; - const maxInputsCount = inputsCount >= possibleInputsCount ? possibleInputsCount : inputsCount; - for (let i = 0; i < maxInputsCount; i++) { - //input - const input = inputs[i]; - const inputIdentifier = i + 1; - - //get input reference - const inputReference = input.reference; - - //get input name - const savedInputsName = this.savedInputsNames[inputReference] ?? false; - input.name = savedInputsName ? savedInputsName : input.name; - - //get type - const inputSourceType = 0; - - //get configured - const isConfigured = 1; - - //get visibility - input.visibility = this.savedInputsTargetVisibility[inputReference] ?? 0; - - //add identifier to the input - input.identifier = inputIdentifier; - - //input service - const inputService = accessory.addService(Service.InputSource, input.name, `Input ${inputIdentifier}`); - inputService - .setCharacteristic(Characteristic.Identifier, inputIdentifier) - .setCharacteristic(Characteristic.Name, input.name) - .setCharacteristic(Characteristic.IsConfigured, isConfigured) - .setCharacteristic(Characteristic.InputSourceType, inputSourceType) - .setCharacteristic(Characteristic.CurrentVisibilityState, input.visibility) + this.inputsConfigured.push(input); + this.televisionService.addLinkedService(inputService); + this.allServices.push(inputService); + }; - inputService.getCharacteristic(Characteristic.ConfiguredName) + //prepare volume service + if (this.volumeControl) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare volume service`); + const volumeServiceName = this.volumeControlNamePrefix ? `${accessoryName} ${this.volumeControlName}` : this.volumeControlName; + if (this.volumeControl === 1) { + this.volumeService = accessory.addService(Service.Lightbulb, `${volumeServiceName}`, 'Volume'); + this.volumeService.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.volumeService.setCharacteristic(Characteristic.ConfiguredName, `${volumeServiceName}`); + this.volumeService.getCharacteristic(Characteristic.Brightness) + .setProps({ + minValue: 0, + maxValue: this.volumeMax + }) .onGet(async () => { - return input.name; + const volume = this.volume; + return volume; }) - .onSet(async (value) => { - if (value === this.savedInputsNames[inputReference]) { - return; - } - - try { - input.name = value; - this.savedInputsNames[inputReference] = value; - await this.saveData(this.inputsNamesFile, this.savedInputsNames); - const debug = !this.enableDebugMode ? false : this.emit('debug', `Saved Input Name: ${value}, Reference: ${inputReference}`); - - //sort inputs - const index = this.inputsConfigured.findIndex(input => input.reference === inputReference); - this.inputsConfigured[index].name = value; - await this.displayOrder(); - } catch (error) { - this.emit('error', `save Input Name error: ${error}`); - } + .onSet(async (volume) => { + this.speakerService.setCharacteristic(Characteristic.Volume, volume); }); - - inputService.getCharacteristic(Characteristic.TargetVisibilityState) + this.volumeService.getCharacteristic(Characteristic.On) .onGet(async () => { - return input.visibility; + const state = !this.mute; + return state; }) .onSet(async (state) => { - if (state === this.savedInputsTargetVisibility[inputReference]) { - return; - } - - try { - input.visibility = state; - this.savedInputsTargetVisibility[inputReference] = state; - await this.saveData(this.inputsTargetVisibilityFile, this.savedInputsTargetVisibility); - const debug = !this.enableDebugMode ? false : this.emit('debug', `Saved Input: ${input.name} Target Visibility: ${state ? 'HIDEN' : 'SHOWN'}`); - } catch (error) { - this.emit('error', `save Input Target Visibility error: ${error}`); - } + this.speakerService.setCharacteristic(Characteristic.Mute, !state); }); - this.inputsConfigured.push(input); - this.televisionService.addLinkedService(inputService); - this.allServices.push(inputService); - }; - - //prepare volume service - if (this.volumeControl) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare volume service`); - const volumeServiceName = this.volumeControlNamePrefix ? `${accessoryName} ${this.volumeControlName}` : this.volumeControlName; - if (this.volumeControl === 1) { - this.volumeService = accessory.addService(Service.Lightbulb, `${volumeServiceName}`, 'Volume'); - this.volumeService.addOptionalCharacteristic(Characteristic.ConfiguredName); - this.volumeService.setCharacteristic(Characteristic.ConfiguredName, `${volumeServiceName}`); - this.volumeService.getCharacteristic(Characteristic.Brightness) - .setProps({ - minValue: 0, - maxValue: this.volumeMax - }) - .onGet(async () => { - const volume = this.volume; - return volume; - }) - .onSet(async (volume) => { - this.speakerService.setCharacteristic(Characteristic.Volume, volume); - }); - this.volumeService.getCharacteristic(Characteristic.On) - .onGet(async () => { - const state = !this.mute; - return state; - }) - .onSet(async (state) => { - this.speakerService.setCharacteristic(Characteristic.Mute, !state); - }); - - this.allServices.push(this.volumeService); - } - - if (this.volumeControl === 2) { - this.volumeServiceFan = accessory.addService(Service.Fan, `${volumeServiceName}`, 'Volume'); - this.volumeServiceFan.addOptionalCharacteristic(Characteristic.ConfiguredName); - this.volumeServiceFan.setCharacteristic(Characteristic.ConfiguredName, `${volumeServiceName}`); - this.volumeServiceFan.getCharacteristic(Characteristic.RotationSpeed) - .setProps({ - minValue: 0, - maxValue: this.volumeMax - }) - .onGet(async () => { - const volume = this.volume; - return volume; - }) - .onSet(async (volume) => { - this.speakerService.setCharacteristic(Characteristic.Volume, volume); - }); - this.volumeServiceFan.getCharacteristic(Characteristic.On) - .onGet(async () => { - const state = !this.mute; - return state; - }) - .onSet(async (state) => { - this.speakerService.setCharacteristic(Characteristic.Mute, !state); - }); - - this.allServices.push(this.volumeServiceFan); - } - }; + this.allServices.push(this.volumeService); + } - //prepare sensor service - if (this.sensorPower) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare power sensor service`); - this.sensorPowerService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Power Sensor`, `Power Sensor`); - this.sensorPowerService.addOptionalCharacteristic(Characteristic.ConfiguredName); - this.sensorPowerService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Power Sensor`); - this.sensorPowerService.getCharacteristic(Characteristic.ContactSensorState) + if (this.volumeControl === 2) { + this.volumeServiceFan = accessory.addService(Service.Fan, `${volumeServiceName}`, 'Volume'); + this.volumeServiceFan.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.volumeServiceFan.setCharacteristic(Characteristic.ConfiguredName, `${volumeServiceName}`); + this.volumeServiceFan.getCharacteristic(Characteristic.RotationSpeed) + .setProps({ + minValue: 0, + maxValue: this.volumeMax + }) .onGet(async () => { - const state = this.power; - return state; + const volume = this.volume; + return volume; + }) + .onSet(async (volume) => { + this.speakerService.setCharacteristic(Characteristic.Volume, volume); }); - - this.allServices.push(this.sensorPowerService); - }; - - if (this.sensorVolume) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare volume sensor service`); - this.sensorVolumeService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Volume Sensor`, `Volume Sensor`); - this.sensorVolumeService.addOptionalCharacteristic(Characteristic.ConfiguredName); - this.sensorVolumeService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Volume Sensor`); - this.sensorVolumeService.getCharacteristic(Characteristic.ContactSensorState) + this.volumeServiceFan.getCharacteristic(Characteristic.On) .onGet(async () => { - const state = this.sensorVolumeState; + const state = !this.mute; return state; + }) + .onSet(async (state) => { + this.speakerService.setCharacteristic(Characteristic.Mute, !state); }); - this.allServices.push(this.sensorVolumeService); - }; + this.allServices.push(this.volumeServiceFan); + } + }; - if (this.sensorMute) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare mute sensor service`); - this.sensorMuteService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Mute Sensor`, `Mute Sensor`); - this.sensorMuteService.addOptionalCharacteristic(Characteristic.ConfiguredName); - this.sensorMuteService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Mute Sensor`); - this.sensorMuteService.getCharacteristic(Characteristic.ContactSensorState) - .onGet(async () => { - const state = this.power ? this.mute : false; - return state; - }); + //prepare sensor service + if (this.sensorPower) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare power sensor service`); + this.sensorPowerService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Power Sensor`, `Power Sensor`); + this.sensorPowerService.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.sensorPowerService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Power Sensor`); + this.sensorPowerService.getCharacteristic(Characteristic.ContactSensorState) + .onGet(async () => { + const state = this.power; + return state; + }); - this.allServices.push(this.sensorMuteService); - }; + this.allServices.push(this.sensorPowerService); + }; - if (this.sensorInput) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare input sensor service`); - this.sensorInputService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Input Sensor`, `Input Sensor`); - this.sensorInputService.addOptionalCharacteristic(Characteristic.ConfiguredName); - this.sensorInputService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Input Sensor`); - this.sensorInputService.getCharacteristic(Characteristic.ContactSensorState) - .onGet(async () => { - const state = this.sensorInputState; - return state; - }); + if (this.sensorVolume) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare volume sensor service`); + this.sensorVolumeService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Volume Sensor`, `Volume Sensor`); + this.sensorVolumeService.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.sensorVolumeService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Volume Sensor`); + this.sensorVolumeService.getCharacteristic(Characteristic.ContactSensorState) + .onGet(async () => { + const state = this.sensorVolumeState; + return state; + }); - this.allServices.push(this.sensorInputService); - }; + this.allServices.push(this.sensorVolumeService); + }; - //prepare sonsor service - const possibleSensorInputsCount = 99 - this.allServices.length; - const maxSensorInputsCount = this.sensorsInputsConfiguredCount >= possibleSensorInputsCount ? possibleSensorInputsCount : this.sensorsInputsConfiguredCount; - if (maxSensorInputsCount > 0) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare inputs sensors services`); - for (let i = 0; i < maxSensorInputsCount; i++) { - //get sensor - const sensorInput = this.sensorsInputsConfigured[i]; + if (this.sensorMute) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare mute sensor service`); + this.sensorMuteService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Mute Sensor`, `Mute Sensor`); + this.sensorMuteService.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.sensorMuteService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Mute Sensor`); + this.sensorMuteService.getCharacteristic(Characteristic.ContactSensorState) + .onGet(async () => { + const state = this.power ? this.mute : false; + return state; + }); - //get sensor name - const sensorInputName = sensorInput.name; + this.allServices.push(this.sensorMuteService); + }; - //get sensor name prefix - const namePrefix = sensorInput.namePrefix || false; + if (this.sensorInput) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare input sensor service`); + this.sensorInputService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Input Sensor`, `Input Sensor`); + this.sensorInputService.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.sensorInputService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Input Sensor`); + this.sensorInputService.getCharacteristic(Characteristic.ContactSensorState) + .onGet(async () => { + const state = this.sensorInputState; + return state; + }); - //get service type - const serviceType = sensorInput.serviceType; + this.allServices.push(this.sensorInputService); + }; - //get service type - const characteristicType = sensorInput.characteristicType; + //prepare sonsor service + const possibleSensorInputsCount = 99 - this.allServices.length; + const maxSensorInputsCount = this.sensorsInputsConfiguredCount >= possibleSensorInputsCount ? possibleSensorInputsCount : this.sensorsInputsConfiguredCount; + if (maxSensorInputsCount > 0) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare inputs sensors services`); + for (let i = 0; i < maxSensorInputsCount; i++) { + //get sensor + const sensorInput = this.sensorsInputsConfigured[i]; - const serviceName = namePrefix ? `${accessoryName} ${sensorInputName}` : sensorInputName; - const sensorInputService = new serviceType(serviceName, `Sensor ${i}`); - sensorInputService.addOptionalCharacteristic(Characteristic.ConfiguredName); - sensorInputService.setCharacteristic(Characteristic.ConfiguredName, serviceName); - sensorInputService.getCharacteristic(characteristicType) - .onGet(async () => { - const state = sensorInput.state - return state; - }); - this.sensorsInputsServices.push(sensorInputService); - this.allServices.push(sensorInputService); - accessory.addService(sensorInputService); - } + //get sensor name + const sensorInputName = sensorInput.name; + + //get sensor name prefix + const namePrefix = sensorInput.namePrefix || false; + + //get service type + const serviceType = sensorInput.serviceType; + + //get service type + const characteristicType = sensorInput.characteristicType; + + const serviceName = namePrefix ? `${accessoryName} ${sensorInputName}` : sensorInputName; + const sensorInputService = new serviceType(serviceName, `Sensor ${i}`); + sensorInputService.addOptionalCharacteristic(Characteristic.ConfiguredName); + sensorInputService.setCharacteristic(Characteristic.ConfiguredName, serviceName); + sensorInputService.getCharacteristic(characteristicType) + .onGet(async () => { + const state = sensorInput.state + return state; + }); + this.sensorsInputsServices.push(sensorInputService); + this.allServices.push(sensorInputService); + accessory.addService(sensorInputService); } + } - //prepare buttons services - const possibleButtonsCount = 99 - this.allServices.length; - const maxButtonsCount = this.buttonsConfiguredCount >= possibleButtonsCount ? possibleButtonsCount : this.buttonsConfiguredCount; - if (maxButtonsCount > 0) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare buttons services`); - for (let i = 0; i < maxButtonsCount; i++) { - //get button - const button = this.buttonsConfigured[i]; + //prepare buttons services + const possibleButtonsCount = 99 - this.allServices.length; + const maxButtonsCount = this.buttonsConfiguredCount >= possibleButtonsCount ? possibleButtonsCount : this.buttonsConfiguredCount; + if (maxButtonsCount > 0) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare buttons services`); + for (let i = 0; i < maxButtonsCount; i++) { + //get button + const button = this.buttonsConfigured[i]; - //get button name - const buttonName = button.name; - - //get button reference - const buttonReference = button.reference; - - //get button name prefix - const namePrefix = button.namePrefix || false; - - //get service type - const serviceType = button.serviceType; - - const serviceName = namePrefix ? `${accessoryName} ${buttonName}` : buttonName; - const buttonService = new serviceType(serviceName, `Button ${i}`); - buttonService.addOptionalCharacteristic(Characteristic.ConfiguredName); - buttonService.setCharacteristic(Characteristic.ConfiguredName, serviceName); - buttonService.getCharacteristic(Characteristic.On) - .onGet(async () => { - const state = button.state; - return state; - }) - .onSet(async (state) => { - try { - const directSound = CONSTANTS.DirectSoundMode[buttonReference] ?? false; - const directSoundModeMode = directSound ? directSound.mode : false; - const directSoundModeSurround = directSound ? directSound.surround : false; - const command = directSound ? directSoundModeMode : buttonReference.substring(1); - const reference = command; - - const set = state ? await this.denon.send(reference) : false; - const set2 = state && directSound ? await this.denon.send(directSoundModeSurround) : false; - const info = this.disableLogInfo || !state ? false : this.emit('message', `set Button Name: ${buttonName}, Reference: ${reference}`); - } catch (error) { - this.emit('error', `set Button error: ${error}`); - }; - }); + //get button name + const buttonName = button.name; - this.buttonsServices.push(buttonService); - this.allServices.push(buttonService); - accessory.addService(buttonService); - }; - }; + //get button reference + const buttonReference = button.reference; + + //get button name prefix + const namePrefix = button.namePrefix || false; - resolve(accessory); - } catch (error) { - reject(error) + //get service type + const serviceType = button.serviceType; + + const serviceName = namePrefix ? `${accessoryName} ${buttonName}` : buttonName; + const buttonService = new serviceType(serviceName, `Button ${i}`); + buttonService.addOptionalCharacteristic(Characteristic.ConfiguredName); + buttonService.setCharacteristic(Characteristic.ConfiguredName, serviceName); + buttonService.getCharacteristic(Characteristic.On) + .onGet(async () => { + const state = button.state; + return state; + }) + .onSet(async (state) => { + try { + const directSound = CONSTANTS.DirectSoundMode[buttonReference] ?? false; + const directSoundModeMode = directSound ? directSound.mode : false; + const directSoundModeSurround = directSound ? directSound.surround : false; + const command = directSound ? directSoundModeMode : buttonReference.substring(1); + const reference = command; + + const set = state ? await this.denon.send(reference) : false; + const set2 = state && directSound ? await this.denon.send(directSoundModeSurround) : false; + const info = this.disableLogInfo || !state ? false : this.emit('message', `set Button Name: ${buttonName}, Reference: ${reference}`); + } catch (error) { + this.emit('error', `set Button error: ${error}`); + }; + }); + + this.buttonsServices.push(buttonService); + this.allServices.push(buttonService); + accessory.addService(buttonService); + }; }; - }); + + return accessory; + } catch (error) { + this.emitDeviceInfo('error', error) + }; } }; diff --git a/src/surround.js b/src/surround.js index 9dc69cd..ae54db2 100644 --- a/src/surround.js +++ b/src/surround.js @@ -337,529 +337,521 @@ class Surround extends EventEmitter { }); }; - displayOrder() { - return new Promise((resolve, reject) => { - try { - switch (this.inputsDisplayOrder) { - case 0: - this.inputsConfigured.sort((a, b) => a.identifier - b.identifier); - break; - case 1: - this.inputsConfigured.sort((a, b) => a.name.localeCompare(b.name)); - break; - case 2: - this.inputsConfigured.sort((a, b) => b.name.localeCompare(a.name)); - break; - case 3: - this.inputsConfigured.sort((a, b) => a.reference.localeCompare(b.reference)); - break; - case 4: - this.inputsConfigured.sort((a, b) => b.reference.localeCompare(a.reference)); - break; - } - const debug = !this.enableDebugMode ? false : this.emit('debug', `Surrounds display order: ${JSON.stringify(this.inputsConfigured, null, 2)}`); - - const displayOrder = this.inputsConfigured.map(input => input.identifier); - this.televisionService.setCharacteristic(Characteristic.DisplayOrder, Encode(1, displayOrder).toString('base64')); - resolve(); - } catch (error) { - reject(error); - }; - }); + async displayOrder() { + try { + switch (this.inputsDisplayOrder) { + case 0: + this.inputsConfigured.sort((a, b) => a.identifier - b.identifier); + break; + case 1: + this.inputsConfigured.sort((a, b) => a.name.localeCompare(b.name)); + break; + case 2: + this.inputsConfigured.sort((a, b) => b.name.localeCompare(a.name)); + break; + case 3: + this.inputsConfigured.sort((a, b) => a.reference.localeCompare(b.reference)); + break; + case 4: + this.inputsConfigured.sort((a, b) => b.reference.localeCompare(a.reference)); + break; + } + const debug = !this.enableDebugMode ? false : this.emit('debug', `Surrounds display order: ${JSON.stringify(this.inputsConfigured, null, 2)}`); + + const displayOrder = this.inputsConfigured.map(input => input.identifier); + this.televisionService.setCharacteristic(Characteristic.DisplayOrder, Encode(1, displayOrder).toString('base64')); + return true; + } catch (error) { + this.emitDeviceInfo('error', error); + }; } - saveData(path, data) { - return new Promise(async (resolve, reject) => { - try { - await fsPromises.writeFile(path, JSON.stringify(data, null, 2)); - const debug = !this.enableDebugMode ? false : this.emit('debug', `Saved data: ${JSON.stringify(data, null, 2)}`); - resolve(); - } catch (error) { - reject(error); - }; - }); + async saveData(path, data) { + try { + await fsPromises.writeFile(path, JSON.stringify(data, null, 2)); + const debug = !this.enableDebugMode ? false : this.emit('debug', `Saved data: ${JSON.stringify(data, null, 2)}`); + return true; + } catch (error) { + this.emitDeviceInfo('error', error); + }; } - readData(path) { - return new Promise(async (resolve, reject) => { - try { - const data = await fsPromises.readFile(path); - resolve(data); - } catch (error) { - reject(`Read saved data error: ${error}`); - }; - }); + async readData(path) { + try { + const data = await fsPromises.readFile(path); + return data; + } catch (error) { + this.emitDeviceInfo('error', `Read saved data error: ${error}`); + }; } //prepare accessory - prepareAccessory(allInputs) { - return new Promise((resolve, reject) => { - try { - //accessory - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare accessory`); - const accessoryName = this.name; - const accessoryUUID = AccessoryUUID.generate(this.serialNumber + this.zone); - const accessoryCategory = Categories.AUDIO_RECEIVER; - const accessory = new Accessory(accessoryName, accessoryUUID, accessoryCategory); - - //information service - const debug1 = !this.enableDebugMode ? false : this.emit('debug', `Prepare information service`); - this.informationService = accessory.getService(Service.AccessoryInformation) - .setCharacteristic(Characteristic.Manufacturer, this.manufacturer) - .setCharacteristic(Characteristic.Model, this.modelName) - .setCharacteristic(Characteristic.SerialNumber, this.serialNumber) - .setCharacteristic(Characteristic.FirmwareRevision, this.firmwareRevision); - this.allServices.push(this.informationService); - - - //prepare television service - const debug2 = !this.enableDebugMode ? false : this.emit('debug', `Prepare television service`); - this.televisionService = accessory.addService(Service.Television, `${accessoryName} Television`, 'Television'); - this.televisionService.setCharacteristic(Characteristic.ConfiguredName, accessoryName); - this.televisionService.setCharacteristic(Characteristic.SleepDiscoveryMode, 1); - - this.televisionService.getCharacteristic(Characteristic.Active) - .onGet(async () => { - const state = this.power; - return state; - }) - .onSet(async (state) => { - if (this.power == state) { - return; - } - - try { - const powerState = this.masterPower ? (state ? 'PWON' : 'PWSTANDBY') : (state ? 'ZMON' : 'ZMOFF'); - await this.denon.send(powerState); - const info = this.disableLogInfo ? false : this.emit('message', `set Power: ${powerState}`); - } catch (error) { - this.emit('error', `set Power error: ${error}`); - }; - }); - - this.televisionService.getCharacteristic(Characteristic.ActiveIdentifier) - .onGet(async () => { - const inputIdentifier = this.inputIdentifier; - return inputIdentifier; - }) - .onSet(async (activeIdentifier) => { - try { - const index = this.inputsConfigured.findIndex(input => input.identifier === activeIdentifier); - const input = this.inputsConfigured[index]; - const inputName = input.name; - const inputMode = input.mode; - const inputReference = input.reference; - const reference = `${inputMode}${inputReference}`; - - switch (this.power) { - case false: - await new Promise(resolve => setTimeout(resolve, 4000)); - const tryAgain = this.power ? this.televisionService.setCharacteristic(Characteristic.ActiveIdentifier, activeIdentifier) : false; - break; - case true: - await this.denon.send(reference); - const info = this.disableLogInfo ? false : this.emit('message', `set Surround Name: ${inputName}, Reference: ${reference}`); - break; - } - } catch (error) { - this.emit('error', `set Surround error: ${error}`); - }; - }); + async prepareAccessory(allInputs) { + try { + //accessory + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare accessory`); + const accessoryName = this.name; + const accessoryUUID = AccessoryUUID.generate(this.serialNumber + this.zone); + const accessoryCategory = Categories.AUDIO_RECEIVER; + const accessory = new Accessory(accessoryName, accessoryUUID, accessoryCategory); + + //information service + const debug1 = !this.enableDebugMode ? false : this.emit('debug', `Prepare information service`); + this.informationService = accessory.getService(Service.AccessoryInformation) + .setCharacteristic(Characteristic.Manufacturer, this.manufacturer) + .setCharacteristic(Characteristic.Model, this.modelName) + .setCharacteristic(Characteristic.SerialNumber, this.serialNumber) + .setCharacteristic(Characteristic.FirmwareRevision, this.firmwareRevision); + this.allServices.push(this.informationService); + + + //prepare television service + const debug2 = !this.enableDebugMode ? false : this.emit('debug', `Prepare television service`); + this.televisionService = accessory.addService(Service.Television, `${accessoryName} Television`, 'Television'); + this.televisionService.setCharacteristic(Characteristic.ConfiguredName, accessoryName); + this.televisionService.setCharacteristic(Characteristic.SleepDiscoveryMode, 1); + + this.televisionService.getCharacteristic(Characteristic.Active) + .onGet(async () => { + const state = this.power; + return state; + }) + .onSet(async (state) => { + if (this.power == state) { + return; + } - this.televisionService.getCharacteristic(Characteristic.RemoteKey) - .onSet(async (command) => { - try { - const rcMedia = this.inputReference === 'SPOTIFY' || this.inputReference === 'BT' || this.inputReference === 'USB/IPOD' || this.inputReference === 'NET' || this.inputReference === 'MPLAY'; - switch (command) { - case Characteristic.RemoteKey.REWIND: - command = rcMedia ? 'NS9E' : 'MN9E'; - break; - case Characteristic.RemoteKey.FAST_FORWARD: - command = rcMedia ? 'NS9D' : 'MN9D'; - break; - case Characteristic.RemoteKey.NEXT_TRACK: - command = rcMedia ? 'MN9D' : 'MN9F'; - break; - case Characteristic.RemoteKey.PREVIOUS_TRACK: - command = rcMedia ? 'MN9E' : 'MN9G'; - break; - case Characteristic.RemoteKey.ARROW_UP: - command = rcMedia ? 'NS90' : 'MNCUP'; - break; - case Characteristic.RemoteKey.ARROW_DOWN: - command = rcMedia ? 'NS91' : 'MNCDN'; - break; - case Characteristic.RemoteKey.ARROW_LEFT: - command = rcMedia ? 'NS92' : 'MNCLT'; - break; - case Characteristic.RemoteKey.ARROW_RIGHT: - command = rcMedia ? 'NS93' : 'MNENT'; - break; - case Characteristic.RemoteKey.SELECT: - command = rcMedia ? 'NS94' : 'MNENT'; - break; - case Characteristic.RemoteKey.BACK: - command = rcMedia ? 'MNRTN' : 'MNRTN'; - break; - case Characteristic.RemoteKey.EXIT: - command = rcMedia ? 'MNRTN' : 'MNRTN'; - break; - case Characteristic.RemoteKey.PLAY_PAUSE: - command = rcMedia ? (this.mediaState ? 'NS9B' : 'NS9A') : 'NS94'; - this.mediaState = !this.mediaState; - break; - case Characteristic.RemoteKey.INFORMATION: - command = this.infoButtonCommand; - break; - } - - await this.denon.send(command); - const info = this.disableLogInfo ? false : this.emit('message', `set Remote Key: ${command}`); - } catch (error) { - this.emit('error', `set Remote Key error: ${error}`); - }; - }); + try { + const powerState = this.masterPower ? (state ? 'PWON' : 'PWSTANDBY') : (state ? 'ZMON' : 'ZMOFF'); + await this.denon.send(powerState); + const info = this.disableLogInfo ? false : this.emit('message', `set Power: ${powerState}`); + } catch (error) { + this.emit('error', `set Power error: ${error}`); + }; + }); + + this.televisionService.getCharacteristic(Characteristic.ActiveIdentifier) + .onGet(async () => { + const inputIdentifier = this.inputIdentifier; + return inputIdentifier; + }) + .onSet(async (activeIdentifier) => { + try { + const index = this.inputsConfigured.findIndex(input => input.identifier === activeIdentifier); + const input = this.inputsConfigured[index]; + const inputName = input.name; + const inputMode = input.mode; + const inputReference = input.reference; + const reference = `${inputMode}${inputReference}`; + + switch (this.power) { + case false: + await new Promise(resolve => setTimeout(resolve, 4000)); + const tryAgain = this.power ? this.televisionService.setCharacteristic(Characteristic.ActiveIdentifier, activeIdentifier) : false; + break; + case true: + await this.denon.send(reference); + const info = this.disableLogInfo ? false : this.emit('message', `set Surround Name: ${inputName}, Reference: ${reference}`); + break; + } + } catch (error) { + this.emit('error', `set Surround error: ${error}`); + }; + }); + + this.televisionService.getCharacteristic(Characteristic.RemoteKey) + .onSet(async (command) => { + try { + const rcMedia = this.inputReference === 'SPOTIFY' || this.inputReference === 'BT' || this.inputReference === 'USB/IPOD' || this.inputReference === 'NET' || this.inputReference === 'MPLAY'; + switch (command) { + case Characteristic.RemoteKey.REWIND: + command = rcMedia ? 'NS9E' : 'MN9E'; + break; + case Characteristic.RemoteKey.FAST_FORWARD: + command = rcMedia ? 'NS9D' : 'MN9D'; + break; + case Characteristic.RemoteKey.NEXT_TRACK: + command = rcMedia ? 'MN9D' : 'MN9F'; + break; + case Characteristic.RemoteKey.PREVIOUS_TRACK: + command = rcMedia ? 'MN9E' : 'MN9G'; + break; + case Characteristic.RemoteKey.ARROW_UP: + command = rcMedia ? 'NS90' : 'MNCUP'; + break; + case Characteristic.RemoteKey.ARROW_DOWN: + command = rcMedia ? 'NS91' : 'MNCDN'; + break; + case Characteristic.RemoteKey.ARROW_LEFT: + command = rcMedia ? 'NS92' : 'MNCLT'; + break; + case Characteristic.RemoteKey.ARROW_RIGHT: + command = rcMedia ? 'NS93' : 'MNENT'; + break; + case Characteristic.RemoteKey.SELECT: + command = rcMedia ? 'NS94' : 'MNENT'; + break; + case Characteristic.RemoteKey.BACK: + command = rcMedia ? 'MNRTN' : 'MNRTN'; + break; + case Characteristic.RemoteKey.EXIT: + command = rcMedia ? 'MNRTN' : 'MNRTN'; + break; + case Characteristic.RemoteKey.PLAY_PAUSE: + command = rcMedia ? (this.mediaState ? 'NS9B' : 'NS9A') : 'NS94'; + this.mediaState = !this.mediaState; + break; + case Characteristic.RemoteKey.INFORMATION: + command = this.infoButtonCommand; + break; + } - this.televisionService.getCharacteristic(Characteristic.PowerModeSelection) - .onSet(async (command) => { - try { - switch (command) { - case Characteristic.PowerModeSelection.SHOW: - command = 'MNOPT'; - break; - case Characteristic.PowerModeSelection.HIDE: - command = 'MNRTN'; - break; - } - - await this.denon.send(command); - const info = this.disableLogInfo ? false : this.emit('message', `set Power Mode Selection: ${command === 'MNOPT' ? 'SHOW' : 'HIDE'}`); - } catch (error) { - this.emit('error', `set Power Mode Selection error: ${error}`); - }; - }); - this.allServices.push(this.televisionService); + await this.denon.send(command); + const info = this.disableLogInfo ? false : this.emit('message', `set Remote Key: ${command}`); + } catch (error) { + this.emit('error', `set Remote Key error: ${error}`); + }; + }); + + this.televisionService.getCharacteristic(Characteristic.PowerModeSelection) + .onSet(async (command) => { + try { + switch (command) { + case Characteristic.PowerModeSelection.SHOW: + command = 'MNOPT'; + break; + case Characteristic.PowerModeSelection.HIDE: + command = 'MNRTN'; + break; + } - //prepare speaker service - const debug3 = !this.enableDebugMode ? false : this.emit('debug', `Prepare speaker service`); - this.speakerService = accessory.addService(Service.TelevisionSpeaker, `${accessoryName} Speaker`, 'Speaker'); - this.speakerService.getCharacteristic(Characteristic.Active) - .onGet(async () => { - const state = this.power; - return state; - }) - .onSet(async (state) => { - }); - this.speakerService.getCharacteristic(Characteristic.VolumeControlType) - .onGet(async () => { - const controlType = this.volumeControlType === 'Relative' ? 1 : 3; //none, relative, relative with current, absolute - const state = 3; - return state; - }) - this.speakerService.getCharacteristic(Characteristic.VolumeSelector) - .onSet(async (command) => { - try { - switch (command) { - case Characteristic.VolumeSelector.INCREMENT: - command = 'MVUP'; - break; - case Characteristic.VolumeSelector.DECREMENT: - command = 'MVDOWN'; - break; - } - - await this.denon.send(command); - const info = this.disableLogInfo ? false : this.emit('message', `set Volume Selector: ${command}`); - } catch (error) { - this.emit('error', `set Volume Selector error: ${error}`); - }; - }); + await this.denon.send(command); + const info = this.disableLogInfo ? false : this.emit('message', `set Power Mode Selection: ${command === 'MNOPT' ? 'SHOW' : 'HIDE'}`); + } catch (error) { + this.emit('error', `set Power Mode Selection error: ${error}`); + }; + }); + this.allServices.push(this.televisionService); + + //prepare speaker service + const debug3 = !this.enableDebugMode ? false : this.emit('debug', `Prepare speaker service`); + this.speakerService = accessory.addService(Service.TelevisionSpeaker, `${accessoryName} Speaker`, 'Speaker'); + this.speakerService.getCharacteristic(Characteristic.Active) + .onGet(async () => { + const state = this.power; + return state; + }) + .onSet(async (state) => { + }); + this.speakerService.getCharacteristic(Characteristic.VolumeControlType) + .onGet(async () => { + const controlType = this.volumeControlType === 'Relative' ? 1 : 3; //none, relative, relative with current, absolute + const state = 3; + return state; + }) + this.speakerService.getCharacteristic(Characteristic.VolumeSelector) + .onSet(async (command) => { + try { + switch (command) { + case Characteristic.VolumeSelector.INCREMENT: + command = 'MVUP'; + break; + case Characteristic.VolumeSelector.DECREMENT: + command = 'MVDOWN'; + break; + } - this.speakerService.getCharacteristic(Characteristic.Volume) - .setProps({ - minValue: 0, - maxValue: this.volumeMax - }) + await this.denon.send(command); + const info = this.disableLogInfo ? false : this.emit('message', `set Volume Selector: ${command}`); + } catch (error) { + this.emit('error', `set Volume Selector error: ${error}`); + }; + }); + + this.speakerService.getCharacteristic(Characteristic.Volume) + .setProps({ + minValue: 0, + maxValue: this.volumeMax + }) + .onGet(async () => { + const volume = this.volume; + return volume; + }) + .onSet(async (value) => { + try { + value = value < 10 ? `0${value}` : value; + const volume = `MV${value}`; + await this.denon.send(volume); + const info = this.disableLogInfo ? false : this.emit('message', `set Volume: ${value - 80}`); + } catch (error) { + this.emit('error', `set Volume error: ${error}`); + }; + }); + + this.speakerService.getCharacteristic(Characteristic.Mute) + .onGet(async () => { + const state = this.mute; + return state; + }) + .onSet(async (state) => { + try { + const muteState = state ? 'MUON' : 'MUOFF'; + await this.denon.send(muteState); + const info = this.disableLogInfo ? false : this.emit('message', `set Mute: ${state ? 'ON' : 'OFF'}`); + } catch (error) { + this.emit('error', `set Mute error: ${error}`); + }; + }); + + this.allServices.push(this.speakerService); + + //prepare inputs service + const debug8 = !this.enableDebugMode ? false : this.emit('debug', `Prepare surrounds services`); + + //check possible inputs count (max 85) + const inputs = allInputs; + const inputsCount = inputs.length; + const possibleInputsCount = 85 - this.allServices.length; + const maxInputsCount = inputsCount >= possibleInputsCount ? possibleInputsCount : inputsCount; + for (let i = 0; i < maxInputsCount; i++) { + //input + const input = inputs[i]; + const inputIdentifier = i + 1; + + //get input reference + const inputReference = input.reference; + + //get input name + const savedInputsName = this.savedInputsNames[inputReference] ?? false; + input.name = savedInputsName ? savedInputsName : input.name; + + //get type + const inputSourceType = 0; + + //get configured + const isConfigured = 1; + + //get visibility + input.visibility = this.savedInputsTargetVisibility[inputReference] ?? 0; + + //add identifier to the input + input.identifier = inputIdentifier; + + //input service + const inputService = accessory.addService(Service.InputSource, input.name, `Surround ${inputIdentifier}`); + inputService + .setCharacteristic(Characteristic.Identifier, inputIdentifier) + .setCharacteristic(Characteristic.Name, input.name) + .setCharacteristic(Characteristic.IsConfigured, isConfigured) + .setCharacteristic(Characteristic.InputSourceType, inputSourceType) + .setCharacteristic(Characteristic.CurrentVisibilityState, input.visibility) + + inputService.getCharacteristic(Characteristic.ConfiguredName) .onGet(async () => { - const volume = this.volume; - return volume; + return input.name; }) .onSet(async (value) => { + if (value === this.savedInputsNames[inputReference]) { + return; + } + try { - value = value < 10 ? `0${value}` : value; - const volume = `MV${value}`; - await this.denon.send(volume); - const info = this.disableLogInfo ? false : this.emit('message', `set Volume: ${value - 80}`); + input.name = value; + this.savedInputsNames[inputReference] = value; + await this.saveData(this.inputsNamesFile, this.savedInputsNames); + const debug = !this.enableDebugMode ? false : this.emit('debug', `Saved Surround Name: ${value}, Reference: ${inputReference}`); + + //sort inputs + const index = this.inputsConfigured.findIndex(input => input.reference === inputReference); + this.inputsConfigured[index].name = value; + await this.displayOrder(); } catch (error) { - this.emit('error', `set Volume error: ${error}`); - }; + this.emit('error', `save Surround Name error: ${error}`); + } }); - this.speakerService.getCharacteristic(Characteristic.Mute) + inputService.getCharacteristic(Characteristic.TargetVisibilityState) .onGet(async () => { - const state = this.mute; - return state; + return input.visibility; }) .onSet(async (state) => { + if (state === this.savedInputsTargetVisibility[inputReference]) { + return; + } + try { - const muteState = state ? 'MUON' : 'MUOFF'; - await this.denon.send(muteState); - const info = this.disableLogInfo ? false : this.emit('message', `set Mute: ${state ? 'ON' : 'OFF'}`); + input.visibility = state; + this.savedInputsTargetVisibility[inputReference] = state; + await this.saveData(this.inputsTargetVisibilityFile, this.savedInputsTargetVisibility); + const debug = !this.enableDebugMode ? false : this.emit('debug', `Saved Surround: ${input.name} Target Visibility: ${state ? 'HIDEN' : 'SHOWN'}`); } catch (error) { - this.emit('error', `set Mute error: ${error}`); - }; + this.emit('error', `save Surround Target Visibility error: ${error}`); + } }); - this.allServices.push(this.speakerService); - - //prepare inputs service - const debug8 = !this.enableDebugMode ? false : this.emit('debug', `Prepare surrounds services`); - - //check possible inputs count (max 85) - const inputs = allInputs; - const inputsCount = inputs.length; - const possibleInputsCount = 85 - this.allServices.length; - const maxInputsCount = inputsCount >= possibleInputsCount ? possibleInputsCount : inputsCount; - for (let i = 0; i < maxInputsCount; i++) { - //input - const input = inputs[i]; - const inputIdentifier = i + 1; - - //get input reference - const inputReference = input.reference; - - //get input name - const savedInputsName = this.savedInputsNames[inputReference] ?? false; - input.name = savedInputsName ? savedInputsName : input.name; - - //get type - const inputSourceType = 0; - - //get configured - const isConfigured = 1; - - //get visibility - input.visibility = this.savedInputsTargetVisibility[inputReference] ?? 0; - - //add identifier to the input - input.identifier = inputIdentifier; - - //input service - const inputService = accessory.addService(Service.InputSource, input.name, `Surround ${inputIdentifier}`); - inputService - .setCharacteristic(Characteristic.Identifier, inputIdentifier) - .setCharacteristic(Characteristic.Name, input.name) - .setCharacteristic(Characteristic.IsConfigured, isConfigured) - .setCharacteristic(Characteristic.InputSourceType, inputSourceType) - .setCharacteristic(Characteristic.CurrentVisibilityState, input.visibility) + this.inputsConfigured.push(input); + this.televisionService.addLinkedService(inputService); + this.allServices.push(inputService); + }; - inputService.getCharacteristic(Characteristic.ConfiguredName) + //prepare volume service + if (this.volumeControl) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare volume service`); + const volumeServiceName = this.volumeControlNamePrefix ? `${accessoryName} ${this.volumeControlName}` : this.volumeControlName; + if (this.volumeControl === 1) { + this.volumeService = accessory.addService(Service.Lightbulb, `${volumeServiceName}`, 'Volume'); + this.volumeService.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.volumeService.setCharacteristic(Characteristic.ConfiguredName, `${volumeServiceName}`); + this.volumeService.getCharacteristic(Characteristic.Brightness) + .setProps({ + minValue: 0, + maxValue: this.volumeMax + }) .onGet(async () => { - return input.name; + const volume = this.volume; + return volume; }) - .onSet(async (value) => { - if (value === this.savedInputsNames[inputReference]) { - return; - } - - try { - input.name = value; - this.savedInputsNames[inputReference] = value; - await this.saveData(this.inputsNamesFile, this.savedInputsNames); - const debug = !this.enableDebugMode ? false : this.emit('debug', `Saved Surround Name: ${value}, Reference: ${inputReference}`); - - //sort inputs - const index = this.inputsConfigured.findIndex(input => input.reference === inputReference); - this.inputsConfigured[index].name = value; - await this.displayOrder(); - } catch (error) { - this.emit('error', `save Surround Name error: ${error}`); - } + .onSet(async (volume) => { + this.speakerService.setCharacteristic(Characteristic.Volume, volume); }); - - inputService.getCharacteristic(Characteristic.TargetVisibilityState) + this.volumeService.getCharacteristic(Characteristic.On) .onGet(async () => { - return input.visibility; + const state = !this.mute; + return state; }) .onSet(async (state) => { - if (state === this.savedInputsTargetVisibility[inputReference]) { - return; - } - - try { - input.visibility = state; - this.savedInputsTargetVisibility[inputReference] = state; - await this.saveData(this.inputsTargetVisibilityFile, this.savedInputsTargetVisibility); - const debug = !this.enableDebugMode ? false : this.emit('debug', `Saved Surround: ${input.name} Target Visibility: ${state ? 'HIDEN' : 'SHOWN'}`); - } catch (error) { - this.emit('error', `save Surround Target Visibility error: ${error}`); - } + this.speakerService.setCharacteristic(Characteristic.Mute, !state); }); - this.inputsConfigured.push(input); - this.televisionService.addLinkedService(inputService); - this.allServices.push(inputService); - }; - - //prepare volume service - if (this.volumeControl) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare volume service`); - const volumeServiceName = this.volumeControlNamePrefix ? `${accessoryName} ${this.volumeControlName}` : this.volumeControlName; - if (this.volumeControl === 1) { - this.volumeService = accessory.addService(Service.Lightbulb, `${volumeServiceName}`, 'Volume'); - this.volumeService.addOptionalCharacteristic(Characteristic.ConfiguredName); - this.volumeService.setCharacteristic(Characteristic.ConfiguredName, `${volumeServiceName}`); - this.volumeService.getCharacteristic(Characteristic.Brightness) - .setProps({ - minValue: 0, - maxValue: this.volumeMax - }) - .onGet(async () => { - const volume = this.volume; - return volume; - }) - .onSet(async (volume) => { - this.speakerService.setCharacteristic(Characteristic.Volume, volume); - }); - this.volumeService.getCharacteristic(Characteristic.On) - .onGet(async () => { - const state = !this.mute; - return state; - }) - .onSet(async (state) => { - this.speakerService.setCharacteristic(Characteristic.Mute, !state); - }); - - this.allServices.push(this.volumeService); - } - - if (this.volumeControl === 2) { - this.volumeServiceFan = accessory.addService(Service.Fan, `${volumeServiceName}`, 'Volume'); - this.volumeServiceFan.addOptionalCharacteristic(Characteristic.ConfiguredName); - this.volumeServiceFan.setCharacteristic(Characteristic.ConfiguredName, `${volumeServiceName}`); - this.volumeServiceFan.getCharacteristic(Characteristic.RotationSpeed) - .setProps({ - minValue: 0, - maxValue: this.volumeMax - }) - .onGet(async () => { - const volume = this.volume; - return volume; - }) - .onSet(async (volume) => { - this.speakerService.setCharacteristic(Characteristic.Volume, volume); - }); - this.volumeServiceFan.getCharacteristic(Characteristic.On) - .onGet(async () => { - const state = !this.mute; - return state; - }) - .onSet(async (state) => { - this.speakerService.setCharacteristic(Characteristic.Mute, !state); - }); - - this.allServices.push(this.volumeServiceFan); - } - }; + this.allServices.push(this.volumeService); + } - //prepare sensor service - if (this.sensorPower) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare power sensor service`); - this.sensorPowerService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Power Sensor`, `Power Sensor`); - this.sensorPowerService.addOptionalCharacteristic(Characteristic.ConfiguredName); - this.sensorPowerService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Power Sensor`); - this.sensorPowerService.getCharacteristic(Characteristic.ContactSensorState) + if (this.volumeControl === 2) { + this.volumeServiceFan = accessory.addService(Service.Fan, `${volumeServiceName}`, 'Volume'); + this.volumeServiceFan.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.volumeServiceFan.setCharacteristic(Characteristic.ConfiguredName, `${volumeServiceName}`); + this.volumeServiceFan.getCharacteristic(Characteristic.RotationSpeed) + .setProps({ + minValue: 0, + maxValue: this.volumeMax + }) .onGet(async () => { - const state = this.power; - return state; + const volume = this.volume; + return volume; + }) + .onSet(async (volume) => { + this.speakerService.setCharacteristic(Characteristic.Volume, volume); }); - - this.allServices.push(this.sensorPowerService); - }; - - if (this.sensorVolume) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare volume sensor service`); - this.sensorVolumeService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Volume Sensor`, `Volume Sensor`); - this.sensorVolumeService.addOptionalCharacteristic(Characteristic.ConfiguredName); - this.sensorVolumeService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Volume Sensor`); - this.sensorVolumeService.getCharacteristic(Characteristic.ContactSensorState) + this.volumeServiceFan.getCharacteristic(Characteristic.On) .onGet(async () => { - const state = this.sensorVolumeState; + const state = !this.mute; return state; + }) + .onSet(async (state) => { + this.speakerService.setCharacteristic(Characteristic.Mute, !state); }); - this.allServices.push(this.sensorVolumeService); - }; + this.allServices.push(this.volumeServiceFan); + } + }; - if (this.sensorMute) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare mute sensor service`); - this.sensorMuteService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Mute Sensor`, `Mute Sensor`); - this.sensorMuteService.addOptionalCharacteristic(Characteristic.ConfiguredName); - this.sensorMuteService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Mute Sensor`); - this.sensorMuteService.getCharacteristic(Characteristic.ContactSensorState) - .onGet(async () => { - const state = this.power ? this.mute : false; - return state; - }); + //prepare sensor service + if (this.sensorPower) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare power sensor service`); + this.sensorPowerService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Power Sensor`, `Power Sensor`); + this.sensorPowerService.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.sensorPowerService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Power Sensor`); + this.sensorPowerService.getCharacteristic(Characteristic.ContactSensorState) + .onGet(async () => { + const state = this.power; + return state; + }); - this.allServices.push(this.sensorMuteService); - }; + this.allServices.push(this.sensorPowerService); + }; - if (this.sensorInput) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare input sensor service`); - this.sensorInputService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Input Sensor`, `Input Sensor`); - this.sensorInputService.addOptionalCharacteristic(Characteristic.ConfiguredName); - this.sensorInputService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Input Sensor`); - this.sensorInputService.getCharacteristic(Characteristic.ContactSensorState) - .onGet(async () => { - const state = this.sensorInputState; - return state; - }); + if (this.sensorVolume) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare volume sensor service`); + this.sensorVolumeService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Volume Sensor`, `Volume Sensor`); + this.sensorVolumeService.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.sensorVolumeService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Volume Sensor`); + this.sensorVolumeService.getCharacteristic(Characteristic.ContactSensorState) + .onGet(async () => { + const state = this.sensorVolumeState; + return state; + }); - this.allServices.push(this.sensorInputService); - }; + this.allServices.push(this.sensorVolumeService); + }; - //prepare sonsor service - const possibleSensorInputsCount = 99 - this.allServices.length; - const maxSensorInputsCount = this.sensorsInputsConfiguredCount >= possibleSensorInputsCount ? possibleSensorInputsCount : this.sensorsInputsConfiguredCount; - if (maxSensorInputsCount > 0) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare inputs sensors services`); - for (let i = 0; i < maxSensorInputsCount; i++) { - //get sensor - const sensorInput = this.sensorsInputsConfigured[i]; + if (this.sensorMute) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare mute sensor service`); + this.sensorMuteService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Mute Sensor`, `Mute Sensor`); + this.sensorMuteService.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.sensorMuteService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Mute Sensor`); + this.sensorMuteService.getCharacteristic(Characteristic.ContactSensorState) + .onGet(async () => { + const state = this.power ? this.mute : false; + return state; + }); - //get sensor name - const sensorInputName = sensorInput.name; + this.allServices.push(this.sensorMuteService); + }; - //get sensor name prefix - const namePrefix = sensorInput.namePrefix || false; + if (this.sensorInput) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare input sensor service`); + this.sensorInputService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Input Sensor`, `Input Sensor`); + this.sensorInputService.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.sensorInputService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Input Sensor`); + this.sensorInputService.getCharacteristic(Characteristic.ContactSensorState) + .onGet(async () => { + const state = this.sensorInputState; + return state; + }); + + this.allServices.push(this.sensorInputService); + }; - //get service type - const serviceType = sensorInput.serviceType; + //prepare sonsor service + const possibleSensorInputsCount = 99 - this.allServices.length; + const maxSensorInputsCount = this.sensorsInputsConfiguredCount >= possibleSensorInputsCount ? possibleSensorInputsCount : this.sensorsInputsConfiguredCount; + if (maxSensorInputsCount > 0) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare inputs sensors services`); + for (let i = 0; i < maxSensorInputsCount; i++) { + //get sensor + const sensorInput = this.sensorsInputsConfigured[i]; - //get service type - const characteristicType = sensorInput.characteristicType; + //get sensor name + const sensorInputName = sensorInput.name; - const serviceName = namePrefix ? `${accessoryName} ${sensorInputName}` : sensorInputName; - const sensorInputService = new serviceType(serviceName, `Sensor ${i}`); - sensorInputService.addOptionalCharacteristic(Characteristic.ConfiguredName); - sensorInputService.setCharacteristic(Characteristic.ConfiguredName, serviceName); - sensorInputService.getCharacteristic(characteristicType) - .onGet(async () => { - const state = sensorInput.state - return state; - }); - this.sensorsInputsServices.push(sensorInputService); - this.allServices.push(sensorInputService); - accessory.addService(sensorInputService); - } + //get sensor name prefix + const namePrefix = sensorInput.namePrefix || false; + + //get service type + const serviceType = sensorInput.serviceType; + + //get service type + const characteristicType = sensorInput.characteristicType; + + const serviceName = namePrefix ? `${accessoryName} ${sensorInputName}` : sensorInputName; + const sensorInputService = new serviceType(serviceName, `Sensor ${i}`); + sensorInputService.addOptionalCharacteristic(Characteristic.ConfiguredName); + sensorInputService.setCharacteristic(Characteristic.ConfiguredName, serviceName); + sensorInputService.getCharacteristic(characteristicType) + .onGet(async () => { + const state = sensorInput.state + return state; + }); + this.sensorsInputsServices.push(sensorInputService); + this.allServices.push(sensorInputService); + accessory.addService(sensorInputService); } + } - resolve(accessory); - } catch (error) { - reject(error) - }; - }); + return accessory; + } catch (error) { + this.emitDeviceInfo('error', error) + }; } }; diff --git a/src/zone2.js b/src/zone2.js index b1620b8..5a216e4 100644 --- a/src/zone2.js +++ b/src/zone2.js @@ -367,557 +367,549 @@ class Zone2 extends EventEmitter { }); }; - displayOrder() { - return new Promise((resolve, reject) => { - try { - switch (this.inputsDisplayOrder) { - case 0: - this.inputsConfigured.sort((a, b) => a.identifier - b.identifier); - break; - case 1: - this.inputsConfigured.sort((a, b) => a.name.localeCompare(b.name)); - break; - case 2: - this.inputsConfigured.sort((a, b) => b.name.localeCompare(a.name)); - break; - case 3: - this.inputsConfigured.sort((a, b) => a.reference.localeCompare(b.reference)); - break; - case 4: - this.inputsConfigured.sort((a, b) => b.reference.localeCompare(a.reference)); - break; - } - const debug = !this.enableDebugMode ? false : this.emit('debug', `Inputs display order: ${JSON.stringify(this.inputsConfigured, null, 2)}`); - - const displayOrder = this.inputsConfigured.map(input => input.identifier); - this.televisionService.setCharacteristic(Characteristic.DisplayOrder, Encode(1, displayOrder).toString('base64')); - resolve(); - } catch (error) { - reject(error); - }; - }); + async displayOrder() { + try { + switch (this.inputsDisplayOrder) { + case 0: + this.inputsConfigured.sort((a, b) => a.identifier - b.identifier); + break; + case 1: + this.inputsConfigured.sort((a, b) => a.name.localeCompare(b.name)); + break; + case 2: + this.inputsConfigured.sort((a, b) => b.name.localeCompare(a.name)); + break; + case 3: + this.inputsConfigured.sort((a, b) => a.reference.localeCompare(b.reference)); + break; + case 4: + this.inputsConfigured.sort((a, b) => b.reference.localeCompare(a.reference)); + break; + } + const debug = !this.enableDebugMode ? false : this.emit('debug', `Inputs display order: ${JSON.stringify(this.inputsConfigured, null, 2)}`); + + const displayOrder = this.inputsConfigured.map(input => input.identifier); + this.televisionService.setCharacteristic(Characteristic.DisplayOrder, Encode(1, displayOrder).toString('base64')); + return true; + } catch (error) { + this.emitDeviceInfo('error', error); + }; } - saveData(path, data) { - return new Promise(async (resolve, reject) => { - try { - await fsPromises.writeFile(path, JSON.stringify(data, null, 2)); - const debug = !this.enableDebugMode ? false : this.emit('debug', `Saved data: ${JSON.stringify(data, null, 2)}`); - resolve(); - } catch (error) { - reject(error); - }; - }); + async saveData(path, data) { + try { + await fsPromises.writeFile(path, JSON.stringify(data, null, 2)); + const debug = !this.enableDebugMode ? false : this.emit('debug', `Saved data: ${JSON.stringify(data, null, 2)}`); + return true; + } catch (error) { + this.emitDeviceInfo('error', error); + }; } - readData(path) { - return new Promise(async (resolve, reject) => { - try { - const data = await fsPromises.readFile(path); - resolve(data); - } catch (error) { - reject(`Read saved data error: ${error}`); - }; - }); + async readData(path) { + try { + const data = await fsPromises.readFile(path); + return data; + } catch (error) { + this.emitDeviceInfo('error', `Read saved data error: ${error}`); + }; } //prepare accessory - prepareAccessory(allInputs) { - return new Promise((resolve, reject) => { - try { - //accessory - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare accessory`); - const accessoryName = this.name; - const accessoryUUID = AccessoryUUID.generate(this.serialNumber + this.zone); - const accessoryCategory = Categories.AUDIO_RECEIVER; - const accessory = new Accessory(accessoryName, accessoryUUID, accessoryCategory); - - //information service - const debug1 = !this.enableDebugMode ? false : this.emit('debug', `Prepare information service`); - this.informationService = accessory.getService(Service.AccessoryInformation) - .setCharacteristic(Characteristic.Manufacturer, this.manufacturer) - .setCharacteristic(Characteristic.Model, this.modelName) - .setCharacteristic(Characteristic.SerialNumber, this.serialNumber) - .setCharacteristic(Characteristic.FirmwareRevision, this.firmwareRevision); - this.allServices.push(this.informationService); - - - //prepare television service - const debug2 = !this.enableDebugMode ? false : this.emit('debug', `Prepare television service`); - this.televisionService = accessory.addService(Service.Television, `${accessoryName} Television`, 'Television'); - this.televisionService.setCharacteristic(Characteristic.ConfiguredName, accessoryName); - this.televisionService.setCharacteristic(Characteristic.SleepDiscoveryMode, 1); - - this.televisionService.getCharacteristic(Characteristic.Active) - .onGet(async () => { - const state = this.power; - return state; - }) - .onSet(async (state) => { - if (this.power == state) { - return; + async prepareAccessory(allInputs) { + try { + //accessory + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare accessory`); + const accessoryName = this.name; + const accessoryUUID = AccessoryUUID.generate(this.serialNumber + this.zone); + const accessoryCategory = Categories.AUDIO_RECEIVER; + const accessory = new Accessory(accessoryName, accessoryUUID, accessoryCategory); + + //information service + const debug1 = !this.enableDebugMode ? false : this.emit('debug', `Prepare information service`); + this.informationService = accessory.getService(Service.AccessoryInformation) + .setCharacteristic(Characteristic.Manufacturer, this.manufacturer) + .setCharacteristic(Characteristic.Model, this.modelName) + .setCharacteristic(Characteristic.SerialNumber, this.serialNumber) + .setCharacteristic(Characteristic.FirmwareRevision, this.firmwareRevision); + this.allServices.push(this.informationService); + + + //prepare television service + const debug2 = !this.enableDebugMode ? false : this.emit('debug', `Prepare television service`); + this.televisionService = accessory.addService(Service.Television, `${accessoryName} Television`, 'Television'); + this.televisionService.setCharacteristic(Characteristic.ConfiguredName, accessoryName); + this.televisionService.setCharacteristic(Characteristic.SleepDiscoveryMode, 1); + + this.televisionService.getCharacteristic(Characteristic.Active) + .onGet(async () => { + const state = this.power; + return state; + }) + .onSet(async (state) => { + if (this.power == state) { + return; + } + + try { + const powerState = this.masterPower ? (state ? 'PWON' : 'PWSTANDBY') : (state ? 'Z2ON' : 'Z2OFF'); + await this.denon.send(powerState); + const info = this.disableLogInfo ? false : this.emit('message', `set Power: ${powerState}`); + } catch (error) { + this.emit('error', `set Power error: ${error}`); + }; + }); + + this.televisionService.getCharacteristic(Characteristic.ActiveIdentifier) + .onGet(async () => { + const inputIdentifier = this.inputIdentifier; + return inputIdentifier; + }) + .onSet(async (activeIdentifier) => { + try { + const index = this.inputsConfigured.findIndex(input => input.identifier === activeIdentifier); + const input = this.inputsConfigured[index]; + const inputName = input.name; + const inputMode = input.mode; + const inputReference = input.reference; + const reference = `${inputMode}${inputReference}`; + + switch (this.power) { + case false: + await new Promise(resolve => setTimeout(resolve, 4000)); + const tryAgain = this.power ? this.televisionService.setCharacteristic(Characteristic.ActiveIdentifier, activeIdentifier) : false; + break; + case true: + await this.denon.send(reference); + const info = this.disableLogInfo ? false : this.emit('message', `set Input Name: ${inputName}, Reference: ${reference}`); + break; + } + } catch (error) { + this.emit('error', `set Input error: ${error}`); + }; + }); + + this.televisionService.getCharacteristic(Characteristic.RemoteKey) + .onSet(async (command) => { + try { + const rcMedia = this.inputReference === 'SPOTIFY' || this.inputReference === 'BT' || this.inputReference === 'USB/IPOD' || this.inputReference === 'NET' || this.inputReference === 'MPLAY'; + switch (command) { + case Characteristic.RemoteKey.REWIND: + command = rcMedia ? 'NS9E' : 'MN9E'; + break; + case Characteristic.RemoteKey.FAST_FORWARD: + command = rcMedia ? 'NS9D' : 'MN9D'; + break; + case Characteristic.RemoteKey.NEXT_TRACK: + command = rcMedia ? 'MN9D' : 'MN9F'; + break; + case Characteristic.RemoteKey.PREVIOUS_TRACK: + command = rcMedia ? 'MN9E' : 'MN9G'; + break; + case Characteristic.RemoteKey.ARROW_UP: + command = rcMedia ? 'NS90' : 'MNCUP'; + break; + case Characteristic.RemoteKey.ARROW_DOWN: + command = rcMedia ? 'NS91' : 'MNCDN'; + break; + case Characteristic.RemoteKey.ARROW_LEFT: + command = rcMedia ? 'NS92' : 'MNCLT'; + break; + case Characteristic.RemoteKey.ARROW_RIGHT: + command = rcMedia ? 'NS93' : 'MNENT'; + break; + case Characteristic.RemoteKey.SELECT: + command = rcMedia ? 'NS94' : 'MNENT'; + break; + case Characteristic.RemoteKey.BACK: + command = rcMedia ? 'MNRTN' : 'MNRTN'; + break; + case Characteristic.RemoteKey.EXIT: + command = rcMedia ? 'MNRTN' : 'MNRTN'; + break; + case Characteristic.RemoteKey.PLAY_PAUSE: + command = rcMedia ? (this.mediaState ? 'NS9B' : 'NS9A') : 'NS94'; + this.mediaState = !this.mediaState; + break; + case Characteristic.RemoteKey.INFORMATION: + command = this.infoButtonCommand; + break; } - try { - const powerState = this.masterPower ? (state ? 'PWON' : 'PWSTANDBY') : (state ? 'Z2ON' : 'Z2OFF'); - await this.denon.send(powerState); - const info = this.disableLogInfo ? false : this.emit('message', `set Power: ${powerState}`); - } catch (error) { - this.emit('error', `set Power error: ${error}`); - }; - }); + await this.denon.send(command); + const info = this.disableLogInfo ? false : this.emit('message', `set Remote Key: ${command}`); + } catch (error) { + this.emit('error', `set Remote Key error: ${error}`); + }; + }); + this.allServices.push(this.televisionService); + + //prepare speaker service + const debug3 = !this.enableDebugMode ? false : this.emit('debug', `Prepare speaker service`); + this.speakerService = accessory.addService(Service.TelevisionSpeaker, `${accessoryName} Speaker`, 'Speaker'); + this.speakerService.getCharacteristic(Characteristic.Active) + .onGet(async () => { + const state = this.power; + return state; + }) + .onSet(async (state) => { + }); + this.speakerService.getCharacteristic(Characteristic.VolumeControlType) + .onGet(async () => { + const controlType = this.volumeControlType === 'Relative' ? 1 : 3; //none, relative, relative with current, absolute + const state = 3; + return state; + }) + this.speakerService.getCharacteristic(Characteristic.VolumeSelector) + .onSet(async (command) => { + try { + switch (command) { + case Characteristic.VolumeSelector.INCREMENT: + command = this.masterVolume ? 'MVUP' : 'Z2UP'; + break; + case Characteristic.VolumeSelector.DECREMENT: + command = this.masterVolume ? 'MVDOWN' : 'Z2DOWN'; + break; + } - this.televisionService.getCharacteristic(Characteristic.ActiveIdentifier) - .onGet(async () => { - const inputIdentifier = this.inputIdentifier; - return inputIdentifier; - }) - .onSet(async (activeIdentifier) => { - try { - const index = this.inputsConfigured.findIndex(input => input.identifier === activeIdentifier); - const input = this.inputsConfigured[index]; - const inputName = input.name; - const inputMode = input.mode; - const inputReference = input.reference; - const reference = `${inputMode}${inputReference}`; - - switch (this.power) { - case false: - await new Promise(resolve => setTimeout(resolve, 4000)); - const tryAgain = this.power ? this.televisionService.setCharacteristic(Characteristic.ActiveIdentifier, activeIdentifier) : false; - break; - case true: - await this.denon.send(reference); - const info = this.disableLogInfo ? false : this.emit('message', `set Input Name: ${inputName}, Reference: ${reference}`); - break; - } - } catch (error) { - this.emit('error', `set Input error: ${error}`); - }; - }); + await this.denon.send(command); + const info = this.disableLogInfo ? false : this.emit('message', `set Volume Selector: ${command}`); + } catch (error) { + this.emit('error', `set Volume Selector error: ${error}`); + }; + }); + + this.speakerService.getCharacteristic(Characteristic.Volume) + .setProps({ + minValue: 0, + maxValue: this.volumeMax + }) + .onGet(async () => { + const volume = this.volume; + return volume; + }) + .onSet(async (value) => { + try { + value = value < 10 ? `0${value}` : value; + const volume = this.masterVolume ? `MV${value}` : `Z2${value}`; + await this.denon.send(volume); + const info = this.disableLogInfo ? false : this.emit('message', `set Volume: ${value - 80}`); + } catch (error) { + this.emit('error', `set Volume error: ${error}`); + }; + }); + + this.speakerService.getCharacteristic(Characteristic.Mute) + .onGet(async () => { + const state = this.mute; + return state; + }) + .onSet(async (state) => { + try { + const muteState = this.masterMute ? (state ? 'MUON' : 'MUOFF') : (state ? 'Z2MUON' : 'Z2MUOFF'); + await this.denon.send(muteState); + const info = this.disableLogInfo ? false : this.emit('message', `set Mute: ${state ? 'ON' : 'OFF'}`); + } catch (error) { + this.emit('error', `set Mute error: ${error}`); + }; + }); - this.televisionService.getCharacteristic(Characteristic.RemoteKey) - .onSet(async (command) => { - try { - const rcMedia = this.inputReference === 'SPOTIFY' || this.inputReference === 'BT' || this.inputReference === 'USB/IPOD' || this.inputReference === 'NET' || this.inputReference === 'MPLAY'; - switch (command) { - case Characteristic.RemoteKey.REWIND: - command = rcMedia ? 'NS9E' : 'MN9E'; - break; - case Characteristic.RemoteKey.FAST_FORWARD: - command = rcMedia ? 'NS9D' : 'MN9D'; - break; - case Characteristic.RemoteKey.NEXT_TRACK: - command = rcMedia ? 'MN9D' : 'MN9F'; - break; - case Characteristic.RemoteKey.PREVIOUS_TRACK: - command = rcMedia ? 'MN9E' : 'MN9G'; - break; - case Characteristic.RemoteKey.ARROW_UP: - command = rcMedia ? 'NS90' : 'MNCUP'; - break; - case Characteristic.RemoteKey.ARROW_DOWN: - command = rcMedia ? 'NS91' : 'MNCDN'; - break; - case Characteristic.RemoteKey.ARROW_LEFT: - command = rcMedia ? 'NS92' : 'MNCLT'; - break; - case Characteristic.RemoteKey.ARROW_RIGHT: - command = rcMedia ? 'NS93' : 'MNENT'; - break; - case Characteristic.RemoteKey.SELECT: - command = rcMedia ? 'NS94' : 'MNENT'; - break; - case Characteristic.RemoteKey.BACK: - command = rcMedia ? 'MNRTN' : 'MNRTN'; - break; - case Characteristic.RemoteKey.EXIT: - command = rcMedia ? 'MNRTN' : 'MNRTN'; - break; - case Characteristic.RemoteKey.PLAY_PAUSE: - command = rcMedia ? (this.mediaState ? 'NS9B' : 'NS9A') : 'NS94'; - this.mediaState = !this.mediaState; - break; - case Characteristic.RemoteKey.INFORMATION: - command = this.infoButtonCommand; - break; - } - - await this.denon.send(command); - const info = this.disableLogInfo ? false : this.emit('message', `set Remote Key: ${command}`); - } catch (error) { - this.emit('error', `set Remote Key error: ${error}`); - }; - }); - this.allServices.push(this.televisionService); + this.allServices.push(this.speakerService); - //prepare speaker service - const debug3 = !this.enableDebugMode ? false : this.emit('debug', `Prepare speaker service`); - this.speakerService = accessory.addService(Service.TelevisionSpeaker, `${accessoryName} Speaker`, 'Speaker'); - this.speakerService.getCharacteristic(Characteristic.Active) - .onGet(async () => { - const state = this.power; - return state; - }) - .onSet(async (state) => { - }); - this.speakerService.getCharacteristic(Characteristic.VolumeControlType) - .onGet(async () => { - const controlType = this.volumeControlType === 'Relative' ? 1 : 3; //none, relative, relative with current, absolute - const state = 3; - return state; - }) - this.speakerService.getCharacteristic(Characteristic.VolumeSelector) - .onSet(async (command) => { - try { - switch (command) { - case Characteristic.VolumeSelector.INCREMENT: - command = this.masterVolume ? 'MVUP' : 'Z2UP'; - break; - case Characteristic.VolumeSelector.DECREMENT: - command = this.masterVolume ? 'MVDOWN' : 'Z2DOWN'; - break; - } - - await this.denon.send(command); - const info = this.disableLogInfo ? false : this.emit('message', `set Volume Selector: ${command}`); - } catch (error) { - this.emit('error', `set Volume Selector error: ${error}`); - }; - }); + //prepare inputs service + const debug8 = !this.enableDebugMode ? false : this.emit('debug', `Prepare inputs services`); - this.speakerService.getCharacteristic(Characteristic.Volume) - .setProps({ - minValue: 0, - maxValue: this.volumeMax - }) + //check possible inputs count (max 85) + const inputs = allInputs; + const inputsCount = inputs.length; + const possibleInputsCount = 85 - this.allServices.length; + const maxInputsCount = inputsCount >= possibleInputsCount ? possibleInputsCount : inputsCount; + for (let i = 0; i < maxInputsCount; i++) { + //input + const input = inputs[i]; + const inputIdentifier = i + 1; + + //get input reference + const inputReference = input.reference; + + //get input name + const savedInputsName = this.savedInputsNames[inputReference] ?? false; + input.name = savedInputsName ? savedInputsName : input.name;; + + //get type + const inputSourceType = 0; + + //get configured + const isConfigured = 1; + + //get visibility + input.visibility = this.savedInputsTargetVisibility[inputReference] ?? 0; + + //add identifier to the input + input.identifier = inputIdentifier; + + //input service + const inputService = accessory.addService(Service.InputSource, input.name, `Input ${inputIdentifier}`); + inputService + .setCharacteristic(Characteristic.Identifier, inputIdentifier) + .setCharacteristic(Characteristic.Name, input.name) + .setCharacteristic(Characteristic.IsConfigured, isConfigured) + .setCharacteristic(Characteristic.InputSourceType, inputSourceType) + .setCharacteristic(Characteristic.CurrentVisibilityState, input.visibility) + + inputService.getCharacteristic(Characteristic.ConfiguredName) .onGet(async () => { - const volume = this.volume; - return volume; + return input.name; }) .onSet(async (value) => { + if (value === this.savedInputsNames[inputReference]) { + return; + } + try { - value = value < 10 ? `0${value}` : value; - const volume = this.masterVolume ? `MV${value}` : `Z2${value}`; - await this.denon.send(volume); - const info = this.disableLogInfo ? false : this.emit('message', `set Volume: ${value - 80}`); + input.name = value; + this.savedInputsNames[inputReference] = value; + await this.saveData(this.inputsNamesFile, this.savedInputsNames); + const debug = !this.enableDebugMode ? false : this.emit('debug', `Saved Input Name: ${value}, Reference: ${inputReference}`); + inputService.updateCharacteristic(Characteristic.Name, input.name); + + //sort inputs + const index = this.inputsConfigured.findIndex(input => input.reference === inputReference); + this.inputsConfigured[index].name = value; + await this.displayOrder(); } catch (error) { - this.emit('error', `set Volume error: ${error}`); - }; + this.emit('error', `save Input Name error: ${error}`); + } }); - this.speakerService.getCharacteristic(Characteristic.Mute) + inputService.getCharacteristic(Characteristic.TargetVisibilityState) .onGet(async () => { - const state = this.mute; - return state; + return input.visibility; }) .onSet(async (state) => { + if (state === this.savedInputsTargetVisibility[inputReference]) { + return; + } + try { - const muteState = this.masterMute ? (state ? 'MUON' : 'MUOFF') : (state ? 'Z2MUON' : 'Z2MUOFF'); - await this.denon.send(muteState); - const info = this.disableLogInfo ? false : this.emit('message', `set Mute: ${state ? 'ON' : 'OFF'}`); + input.visibility = state; + this.savedInputsTargetVisibility[inputReference] = state; + await this.saveData(this.inputsTargetVisibilityFile, this.savedInputsTargetVisibility); + const debug = !this.enableDebugMode ? false : this.emit('debug', `Saved Input: ${input.name} Target Visibility: ${state ? 'HIDEN' : 'SHOWN'}`); } catch (error) { - this.emit('error', `set Mute error: ${error}`); - }; + this.emit('error', `save Input Target Visibility error: ${error}`); + } }); - this.allServices.push(this.speakerService); - - //prepare inputs service - const debug8 = !this.enableDebugMode ? false : this.emit('debug', `Prepare inputs services`); - - //check possible inputs count (max 85) - const inputs = allInputs; - const inputsCount = inputs.length; - const possibleInputsCount = 85 - this.allServices.length; - const maxInputsCount = inputsCount >= possibleInputsCount ? possibleInputsCount : inputsCount; - for (let i = 0; i < maxInputsCount; i++) { - //input - const input = inputs[i]; - const inputIdentifier = i + 1; - - //get input reference - const inputReference = input.reference; - - //get input name - const savedInputsName = this.savedInputsNames[inputReference] ?? false; - input.name = savedInputsName ? savedInputsName : input.name;; - - //get type - const inputSourceType = 0; - - //get configured - const isConfigured = 1; - - //get visibility - input.visibility = this.savedInputsTargetVisibility[inputReference] ?? 0; - - //add identifier to the input - input.identifier = inputIdentifier; - - //input service - const inputService = accessory.addService(Service.InputSource, input.name, `Input ${inputIdentifier}`); - inputService - .setCharacteristic(Characteristic.Identifier, inputIdentifier) - .setCharacteristic(Characteristic.Name, input.name) - .setCharacteristic(Characteristic.IsConfigured, isConfigured) - .setCharacteristic(Characteristic.InputSourceType, inputSourceType) - .setCharacteristic(Characteristic.CurrentVisibilityState, input.visibility) + this.inputsConfigured.push(input); + this.televisionService.addLinkedService(inputService); + this.allServices.push(inputService); + }; - inputService.getCharacteristic(Characteristic.ConfiguredName) + //prepare volume service + if (this.volumeControl) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare volume service`); + const volumeServiceName = this.volumeControlNamePrefix ? `${accessoryName} ${this.volumeControlName}` : this.volumeControlName; + if (this.volumeControl === 1) { + this.volumeService = accessory.addService(Service.Lightbulb, `${volumeServiceName}`, 'Volume'); + this.volumeService.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.volumeService.setCharacteristic(Characteristic.ConfiguredName, `${volumeServiceName}`); + this.volumeService.getCharacteristic(Characteristic.Brightness) + .setProps({ + minValue: 0, + maxValue: this.volumeMax + }) .onGet(async () => { - return input.name; + const volume = this.volume; + return volume; }) - .onSet(async (value) => { - if (value === this.savedInputsNames[inputReference]) { - return; - } - - try { - input.name = value; - this.savedInputsNames[inputReference] = value; - await this.saveData(this.inputsNamesFile, this.savedInputsNames); - const debug = !this.enableDebugMode ? false : this.emit('debug', `Saved Input Name: ${value}, Reference: ${inputReference}`); - inputService.updateCharacteristic(Characteristic.Name, input.name); - - //sort inputs - const index = this.inputsConfigured.findIndex(input => input.reference === inputReference); - this.inputsConfigured[index].name = value; - await this.displayOrder(); - } catch (error) { - this.emit('error', `save Input Name error: ${error}`); - } + .onSet(async (volume) => { + this.speakerService.setCharacteristic(Characteristic.Volume, volume); }); - - inputService.getCharacteristic(Characteristic.TargetVisibilityState) + this.volumeService.getCharacteristic(Characteristic.On) .onGet(async () => { - return input.visibility; + const state = !this.mute; + return state; }) .onSet(async (state) => { - if (state === this.savedInputsTargetVisibility[inputReference]) { - return; - } - - try { - input.visibility = state; - this.savedInputsTargetVisibility[inputReference] = state; - await this.saveData(this.inputsTargetVisibilityFile, this.savedInputsTargetVisibility); - const debug = !this.enableDebugMode ? false : this.emit('debug', `Saved Input: ${input.name} Target Visibility: ${state ? 'HIDEN' : 'SHOWN'}`); - } catch (error) { - this.emit('error', `save Input Target Visibility error: ${error}`); - } + this.speakerService.setCharacteristic(Characteristic.Mute, !state); }); - this.inputsConfigured.push(input); - this.televisionService.addLinkedService(inputService); - this.allServices.push(inputService); - }; - - //prepare volume service - if (this.volumeControl) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare volume service`); - const volumeServiceName = this.volumeControlNamePrefix ? `${accessoryName} ${this.volumeControlName}` : this.volumeControlName; - if (this.volumeControl === 1) { - this.volumeService = accessory.addService(Service.Lightbulb, `${volumeServiceName}`, 'Volume'); - this.volumeService.addOptionalCharacteristic(Characteristic.ConfiguredName); - this.volumeService.setCharacteristic(Characteristic.ConfiguredName, `${volumeServiceName}`); - this.volumeService.getCharacteristic(Characteristic.Brightness) - .setProps({ - minValue: 0, - maxValue: this.volumeMax - }) - .onGet(async () => { - const volume = this.volume; - return volume; - }) - .onSet(async (volume) => { - this.speakerService.setCharacteristic(Characteristic.Volume, volume); - }); - this.volumeService.getCharacteristic(Characteristic.On) - .onGet(async () => { - const state = !this.mute; - return state; - }) - .onSet(async (state) => { - this.speakerService.setCharacteristic(Characteristic.Mute, !state); - }); - - this.allServices.push(this.volumeService); - } - - if (this.volumeControl === 2) { - this.volumeServiceFan = accessory.addService(Service.Fan, `${volumeServiceName}`, 'Volume'); - this.volumeServiceFan.addOptionalCharacteristic(Characteristic.ConfiguredName); - this.volumeServiceFan.setCharacteristic(Characteristic.ConfiguredName, `${volumeServiceName}`); - this.volumeServiceFan.getCharacteristic(Characteristic.RotationSpeed) - .setProps({ - minValue: 0, - maxValue: this.volumeMax - }) - .onGet(async () => { - const volume = this.volume; - return volume; - }) - .onSet(async (volume) => { - this.speakerService.setCharacteristic(Characteristic.Volume, volume); - }); - this.volumeServiceFan.getCharacteristic(Characteristic.On) - .onGet(async () => { - const state = !this.mute; - return state; - }) - .onSet(async (state) => { - this.speakerService.setCharacteristic(Characteristic.Mute, !state); - }); - - this.allServices.push(this.volumeServiceFan); - } - }; + this.allServices.push(this.volumeService); + } - //prepare sensor service - if (this.sensorPower) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare power sensor service`); - this.sensorPowerService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Power Sensor`, `Power Sensor`); - this.sensorPowerService.addOptionalCharacteristic(Characteristic.ConfiguredName); - this.sensorPowerService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Power Sensor`); - this.sensorPowerService.getCharacteristic(Characteristic.ContactSensorState) + if (this.volumeControl === 2) { + this.volumeServiceFan = accessory.addService(Service.Fan, `${volumeServiceName}`, 'Volume'); + this.volumeServiceFan.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.volumeServiceFan.setCharacteristic(Characteristic.ConfiguredName, `${volumeServiceName}`); + this.volumeServiceFan.getCharacteristic(Characteristic.RotationSpeed) + .setProps({ + minValue: 0, + maxValue: this.volumeMax + }) .onGet(async () => { - const state = this.power; - return state; + const volume = this.volume; + return volume; + }) + .onSet(async (volume) => { + this.speakerService.setCharacteristic(Characteristic.Volume, volume); }); - - this.allServices.push(this.sensorPowerService); - }; - - if (this.sensorVolume) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare volume sensor service`); - this.sensorVolumeService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Volume Sensor`, `Volume Sensor`); - this.sensorVolumeService.addOptionalCharacteristic(Characteristic.ConfiguredName); - this.sensorVolumeService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Volume Sensor`); - this.sensorVolumeService.getCharacteristic(Characteristic.ContactSensorState) + this.volumeServiceFan.getCharacteristic(Characteristic.On) .onGet(async () => { - const state = this.sensorVolumeState; + const state = !this.mute; return state; + }) + .onSet(async (state) => { + this.speakerService.setCharacteristic(Characteristic.Mute, !state); }); - this.allServices.push(this.sensorVolumeService); - }; + this.allServices.push(this.volumeServiceFan); + } + }; - if (this.sensorMute) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare mute sensor service`); - this.sensorMuteService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Mute Sensor`, `Mute Sensor`); - this.sensorMuteService.addOptionalCharacteristic(Characteristic.ConfiguredName); - this.sensorMuteService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Mute Sensor`); - this.sensorMuteService.getCharacteristic(Characteristic.ContactSensorState) - .onGet(async () => { - const state = this.power ? this.mute : false; - return state; - }); + //prepare sensor service + if (this.sensorPower) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare power sensor service`); + this.sensorPowerService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Power Sensor`, `Power Sensor`); + this.sensorPowerService.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.sensorPowerService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Power Sensor`); + this.sensorPowerService.getCharacteristic(Characteristic.ContactSensorState) + .onGet(async () => { + const state = this.power; + return state; + }); - this.allServices.push(this.sensorMuteService); - }; + this.allServices.push(this.sensorPowerService); + }; - if (this.sensorInput) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare input sensor service`); - this.sensorInputService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Input Sensor`, `Input Sensor`); - this.sensorInputService.addOptionalCharacteristic(Characteristic.ConfiguredName); - this.sensorInputService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Input Sensor`); - this.sensorInputService.getCharacteristic(Characteristic.ContactSensorState) - .onGet(async () => { - const state = this.sensorInputState; - return state; - }); + if (this.sensorVolume) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare volume sensor service`); + this.sensorVolumeService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Volume Sensor`, `Volume Sensor`); + this.sensorVolumeService.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.sensorVolumeService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Volume Sensor`); + this.sensorVolumeService.getCharacteristic(Characteristic.ContactSensorState) + .onGet(async () => { + const state = this.sensorVolumeState; + return state; + }); - this.allServices.push(this.sensorInputService); - }; + this.allServices.push(this.sensorVolumeService); + }; - //prepare sonsor service - const possibleSensorInputsCount = 99 - this.allServices.length; - const maxSensorInputsCount = this.sensorsInputsConfiguredCount >= possibleSensorInputsCount ? possibleSensorInputsCount : this.sensorsInputsConfiguredCount; - if (maxSensorInputsCount > 0) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare inputs sensors services`); - for (let i = 0; i < maxSensorInputsCount; i++) { - //get sensor - const sensorInput = this.sensorsInputsConfigured[i]; + if (this.sensorMute) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare mute sensor service`); + this.sensorMuteService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Mute Sensor`, `Mute Sensor`); + this.sensorMuteService.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.sensorMuteService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Mute Sensor`); + this.sensorMuteService.getCharacteristic(Characteristic.ContactSensorState) + .onGet(async () => { + const state = this.power ? this.mute : false; + return state; + }); + + this.allServices.push(this.sensorMuteService); + }; + + if (this.sensorInput) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare input sensor service`); + this.sensorInputService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Input Sensor`, `Input Sensor`); + this.sensorInputService.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.sensorInputService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Input Sensor`); + this.sensorInputService.getCharacteristic(Characteristic.ContactSensorState) + .onGet(async () => { + const state = this.sensorInputState; + return state; + }); - //get sensor name - const sensorInputName = sensorInput.name; + this.allServices.push(this.sensorInputService); + }; - //get sensor name prefix - const namePrefix = sensorInput.namePrefix || false; + //prepare sonsor service + const possibleSensorInputsCount = 99 - this.allServices.length; + const maxSensorInputsCount = this.sensorsInputsConfiguredCount >= possibleSensorInputsCount ? possibleSensorInputsCount : this.sensorsInputsConfiguredCount; + if (maxSensorInputsCount > 0) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare inputs sensors services`); + for (let i = 0; i < maxSensorInputsCount; i++) { + //get sensor + const sensorInput = this.sensorsInputsConfigured[i]; - //get service type - const serviceType = sensorInput.serviceType; + //get sensor name + const sensorInputName = sensorInput.name; - //get service type - const characteristicType = sensorInput.characteristicType; + //get sensor name prefix + const namePrefix = sensorInput.namePrefix || false; - const serviceName = namePrefix ? `${accessoryName} ${sensorInputName}` : sensorInputName; - const sensorInputService = new serviceType(serviceName, `Sensor ${i}`); - sensorInputService.addOptionalCharacteristic(Characteristic.ConfiguredName); - sensorInputService.setCharacteristic(Characteristic.ConfiguredName, serviceName); - sensorInputService.getCharacteristic(characteristicType) - .onGet(async () => { - const state = sensorInput.state - return state; - }); - this.sensorsInputsServices.push(sensorInputService); - this.allServices.push(sensorInputService); - accessory.addService(sensorInputService); - } + //get service type + const serviceType = sensorInput.serviceType; + + //get service type + const characteristicType = sensorInput.characteristicType; + + const serviceName = namePrefix ? `${accessoryName} ${sensorInputName}` : sensorInputName; + const sensorInputService = new serviceType(serviceName, `Sensor ${i}`); + sensorInputService.addOptionalCharacteristic(Characteristic.ConfiguredName); + sensorInputService.setCharacteristic(Characteristic.ConfiguredName, serviceName); + sensorInputService.getCharacteristic(characteristicType) + .onGet(async () => { + const state = sensorInput.state + return state; + }); + this.sensorsInputsServices.push(sensorInputService); + this.allServices.push(sensorInputService); + accessory.addService(sensorInputService); } + } - //prepare buttons services - const possibleButtonsCount = 99 - this.allServices.length; - const maxButtonsCount = this.buttonsConfiguredCount >= possibleButtonsCount ? possibleButtonsCount : this.buttonsConfiguredCount; - if (maxButtonsCount > 0) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare buttons services`); - for (let i = 0; i < maxButtonsCount; i++) { - //get button - const button = this.buttonsConfigured[i]; + //prepare buttons services + const possibleButtonsCount = 99 - this.allServices.length; + const maxButtonsCount = this.buttonsConfiguredCount >= possibleButtonsCount ? possibleButtonsCount : this.buttonsConfiguredCount; + if (maxButtonsCount > 0) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare buttons services`); + for (let i = 0; i < maxButtonsCount; i++) { + //get button + const button = this.buttonsConfigured[i]; - //get button name - const buttonName = button.name; - - //get button reference - const buttonReference = button.reference; - - //get button name prefix - const namePrefix = button.namePrefix || false; - - //get service type - const serviceType = button.serviceType; - - const serviceName = namePrefix ? `${accessoryName} ${buttonName}` : buttonName; - const buttonService = new serviceType(serviceName, `Button ${i}`); - buttonService.addOptionalCharacteristic(Characteristic.ConfiguredName); - buttonService.setCharacteristic(Characteristic.ConfiguredName, serviceName); - buttonService.getCharacteristic(Characteristic.On) - .onGet(async () => { - const state = button.state; - return state; - }) - .onSet(async (state) => { - try { - const reference = `Z2${buttonReference.substring(1)}`; - const set = state ? await this.denon.send(reference) : false; - const info = this.disableLogInfo || !state ? false : this.emit('message', `set Button Name: ${buttonName}, Reference: ${reference}`); - } catch (error) { - this.emit('error', `set Button error: ${error}`); - }; - }); + //get button name + const buttonName = button.name; - this.buttonsServices.push(buttonService); - this.allServices.push(buttonService); - accessory.addService(buttonService); - }; - }; + //get button reference + const buttonReference = button.reference; - resolve(accessory); - } catch (error) { - reject(error) + //get button name prefix + const namePrefix = button.namePrefix || false; + + //get service type + const serviceType = button.serviceType; + + const serviceName = namePrefix ? `${accessoryName} ${buttonName}` : buttonName; + const buttonService = new serviceType(serviceName, `Button ${i}`); + buttonService.addOptionalCharacteristic(Characteristic.ConfiguredName); + buttonService.setCharacteristic(Characteristic.ConfiguredName, serviceName); + buttonService.getCharacteristic(Characteristic.On) + .onGet(async () => { + const state = button.state; + return state; + }) + .onSet(async (state) => { + try { + const reference = `Z2${buttonReference.substring(1)}`; + const set = state ? await this.denon.send(reference) : false; + const info = this.disableLogInfo || !state ? false : this.emit('message', `set Button Name: ${buttonName}, Reference: ${reference}`); + } catch (error) { + this.emit('error', `set Button error: ${error}`); + }; + }); + + this.buttonsServices.push(buttonService); + this.allServices.push(buttonService); + accessory.addService(buttonService); + }; }; - }); + + return accessory; + } catch (error) { + this.emitDeviceInfo('error', error) + }; } }; diff --git a/src/zone3.js b/src/zone3.js index 36a8193..364dd1e 100644 --- a/src/zone3.js +++ b/src/zone3.js @@ -367,556 +367,548 @@ class Zone3 extends EventEmitter { }); }; - displayOrder() { - return new Promise((resolve, reject) => { - try { - switch (this.inputsDisplayOrder) { - case 0: - this.inputsConfigured.sort((a, b) => a.identifier - b.identifier); - break; - case 1: - this.inputsConfigured.sort((a, b) => a.name.localeCompare(b.name)); - break; - case 2: - this.inputsConfigured.sort((a, b) => b.name.localeCompare(a.name)); - break; - case 3: - this.inputsConfigured.sort((a, b) => a.reference.localeCompare(b.reference)); - break; - case 4: - this.inputsConfigured.sort((a, b) => b.reference.localeCompare(a.reference)); - break; - } - const debug = !this.enableDebugMode ? false : this.emit('debug', `Inputs display order: ${JSON.stringify(this.inputsConfigured, null, 2)}`); - - const displayOrder = this.inputsConfigured.map(input => input.identifier); - this.televisionService.setCharacteristic(Characteristic.DisplayOrder, Encode(1, displayOrder).toString('base64')); - resolve(); - } catch (error) { - reject(error); - }; - }); + async displayOrder() { + try { + switch (this.inputsDisplayOrder) { + case 0: + this.inputsConfigured.sort((a, b) => a.identifier - b.identifier); + break; + case 1: + this.inputsConfigured.sort((a, b) => a.name.localeCompare(b.name)); + break; + case 2: + this.inputsConfigured.sort((a, b) => b.name.localeCompare(a.name)); + break; + case 3: + this.inputsConfigured.sort((a, b) => a.reference.localeCompare(b.reference)); + break; + case 4: + this.inputsConfigured.sort((a, b) => b.reference.localeCompare(a.reference)); + break; + } + const debug = !this.enableDebugMode ? false : this.emit('debug', `Inputs display order: ${JSON.stringify(this.inputsConfigured, null, 2)}`); + + const displayOrder = this.inputsConfigured.map(input => input.identifier); + this.televisionService.setCharacteristic(Characteristic.DisplayOrder, Encode(1, displayOrder).toString('base64')); + return true; + } catch (error) { + this.emitDeviceInfo('error', error); + }; } - saveData(path, data) { - return new Promise(async (resolve, reject) => { - try { - await fsPromises.writeFile(path, JSON.stringify(data, null, 2)); - const debug = !this.enableDebugMode ? false : this.emit('debug', `Saved data: ${JSON.stringify(data, null, 2)}`); - resolve(); - } catch (error) { - reject(error); - }; - }); + async saveData(path, data) { + try { + await fsPromises.writeFile(path, JSON.stringify(data, null, 2)); + const debug = !this.enableDebugMode ? false : this.emit('debug', `Saved data: ${JSON.stringify(data, null, 2)}`); + return true; + } catch (error) { + this.emitDeviceInfo('error', error); + }; } - readData(path) { - return new Promise(async (resolve, reject) => { - try { - const data = await fsPromises.readFile(path); - resolve(data); - } catch (error) { - reject(`Read saved data error: ${error}`); - }; - }); + async readData(path) { + try { + const data = await fsPromises.readFile(path); + return data; + } catch (error) { + this.emitDeviceInfo('error', `Read saved data error: ${error}`); + }; } //prepare accessory - prepareAccessory(allInputs) { - return new Promise((resolve, reject) => { - try { - //accessory - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare accessory`); - const accessoryName = this.name; - const accessoryUUID = AccessoryUUID.generate(this.serialNumber + this.zone); - const accessoryCategory = Categories.AUDIO_RECEIVER; - const accessory = new Accessory(accessoryName, accessoryUUID, accessoryCategory); - - //information service - const debug1 = !this.enableDebugMode ? false : this.emit('debug', `Prepare information service`); - this.informationService = accessory.getService(Service.AccessoryInformation) - .setCharacteristic(Characteristic.Manufacturer, this.manufacturer) - .setCharacteristic(Characteristic.Model, this.modelName) - .setCharacteristic(Characteristic.SerialNumber, this.serialNumber) - .setCharacteristic(Characteristic.FirmwareRevision, this.firmwareRevision); - this.allServices.push(this.informationService); - - - //prepare television service - const debug2 = !this.enableDebugMode ? false : this.emit('debug', `Prepare television service`); - this.televisionService = accessory.addService(Service.Television, `${accessoryName} Television`, 'Television'); - this.televisionService.setCharacteristic(Characteristic.ConfiguredName, accessoryName); - this.televisionService.setCharacteristic(Characteristic.SleepDiscoveryMode, 1); - - this.televisionService.getCharacteristic(Characteristic.Active) - .onGet(async () => { - const state = this.power; - return state; - }) - .onSet(async (state) => { - if (this.power == state) { - return; + async prepareAccessory(allInputs) { + try { + //accessory + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare accessory`); + const accessoryName = this.name; + const accessoryUUID = AccessoryUUID.generate(this.serialNumber + this.zone); + const accessoryCategory = Categories.AUDIO_RECEIVER; + const accessory = new Accessory(accessoryName, accessoryUUID, accessoryCategory); + + //information service + const debug1 = !this.enableDebugMode ? false : this.emit('debug', `Prepare information service`); + this.informationService = accessory.getService(Service.AccessoryInformation) + .setCharacteristic(Characteristic.Manufacturer, this.manufacturer) + .setCharacteristic(Characteristic.Model, this.modelName) + .setCharacteristic(Characteristic.SerialNumber, this.serialNumber) + .setCharacteristic(Characteristic.FirmwareRevision, this.firmwareRevision); + this.allServices.push(this.informationService); + + + //prepare television service + const debug2 = !this.enableDebugMode ? false : this.emit('debug', `Prepare television service`); + this.televisionService = accessory.addService(Service.Television, `${accessoryName} Television`, 'Television'); + this.televisionService.setCharacteristic(Characteristic.ConfiguredName, accessoryName); + this.televisionService.setCharacteristic(Characteristic.SleepDiscoveryMode, 1); + + this.televisionService.getCharacteristic(Characteristic.Active) + .onGet(async () => { + const state = this.power; + return state; + }) + .onSet(async (state) => { + if (this.power == state) { + return; + } + + try { + const powerState = this.masterPower ? (state ? 'PWON' : 'PWSTANDBY') : (state ? 'Z3ON' : 'Z3OFF'); + await this.denon.send(powerState); + const info = this.disableLogInfo ? false : this.emit('message', `set Power: ${powerState}`); + } catch (error) { + this.emit('error', `set Power error: ${error}`); + }; + }); + + this.televisionService.getCharacteristic(Characteristic.ActiveIdentifier) + .onGet(async () => { + const inputIdentifier = this.inputIdentifier; + return inputIdentifier; + }) + .onSet(async (activeIdentifier) => { + try { + const index = this.inputsConfigured.findIndex(input => input.identifier === activeIdentifier); + const input = this.inputsConfigured[index]; + const inputName = input.name; + const inputMode = input.mode; + const inputReference = input.reference; + const reference = `${inputMode}${inputReference}`; + + switch (this.power) { + case false: + await new Promise(resolve => setTimeout(resolve, 4000)); + const tryAgain = this.power ? this.televisionService.setCharacteristic(Characteristic.ActiveIdentifier, activeIdentifier) : false; + break; + case true: + await this.denon.send(reference); + const info = this.disableLogInfo ? false : this.emit('message', `set Input Name: ${inputName}, Reference: ${reference}`); + break; + } + } catch (error) { + this.emit('error', `set Input error: ${error}`); + }; + }); + + this.televisionService.getCharacteristic(Characteristic.RemoteKey) + .onSet(async (command) => { + try { + const rcMedia = this.inputReference === 'SPOTIFY' || this.inputReference === 'BT' || this.inputReference === 'USB/IPOD' || this.inputReference === 'NET' || this.inputReference === 'MPLAY'; + switch (command) { + case Characteristic.RemoteKey.REWIND: + command = rcMedia ? 'NS9E' : 'MN9E'; + break; + case Characteristic.RemoteKey.FAST_FORWARD: + command = rcMedia ? 'NS9D' : 'MN9D'; + break; + case Characteristic.RemoteKey.NEXT_TRACK: + command = rcMedia ? 'MN9D' : 'MN9F'; + break; + case Characteristic.RemoteKey.PREVIOUS_TRACK: + command = rcMedia ? 'MN9E' : 'MN9G'; + break; + case Characteristic.RemoteKey.ARROW_UP: + command = rcMedia ? 'NS90' : 'MNCUP'; + break; + case Characteristic.RemoteKey.ARROW_DOWN: + command = rcMedia ? 'NS91' : 'MNCDN'; + break; + case Characteristic.RemoteKey.ARROW_LEFT: + command = rcMedia ? 'NS92' : 'MNCLT'; + break; + case Characteristic.RemoteKey.ARROW_RIGHT: + command = rcMedia ? 'NS93' : 'MNENT'; + break; + case Characteristic.RemoteKey.SELECT: + command = rcMedia ? 'NS94' : 'MNENT'; + break; + case Characteristic.RemoteKey.BACK: + command = rcMedia ? 'MNRTN' : 'MNRTN'; + break; + case Characteristic.RemoteKey.EXIT: + command = rcMedia ? 'MNRTN' : 'MNRTN'; + break; + case Characteristic.RemoteKey.PLAY_PAUSE: + command = rcMedia ? (this.mediaState ? 'NS9B' : 'NS9A') : 'NS94'; + this.mediaState = !this.mediaState; + break; + case Characteristic.RemoteKey.INFORMATION: + command = this.infoButtonCommand; + break; } - try { - const powerState = this.masterPower ? (state ? 'PWON' : 'PWSTANDBY') : (state ? 'Z3ON' : 'Z3OFF'); - await this.denon.send(powerState); - const info = this.disableLogInfo ? false : this.emit('message', `set Power: ${powerState}`); - } catch (error) { - this.emit('error', `set Power error: ${error}`); - }; - }); + await this.denon.send(command); + const info = this.disableLogInfo ? false : this.emit('message', `set Remote Key: ${command}`); + } catch (error) { + this.emit('error', `set Remote Key error: ${error}`); + }; + }); + this.allServices.push(this.televisionService); + + //prepare speaker service + const debug3 = !this.enableDebugMode ? false : this.emit('debug', `Prepare speaker service`); + this.speakerService = accessory.addService(Service.TelevisionSpeaker, `${accessoryName} Speaker`, 'Speaker'); + this.speakerService.getCharacteristic(Characteristic.Active) + .onGet(async () => { + const state = this.power; + return state; + }) + .onSet(async (state) => { + }); + this.speakerService.getCharacteristic(Characteristic.VolumeControlType) + .onGet(async () => { + const controlType = this.volumeControlType === 'Relative' ? 1 : 3; //none, relative, relative with current, absolute + const state = 3; + return state; + }) + this.speakerService.getCharacteristic(Characteristic.VolumeSelector) + .onSet(async (command) => { + try { + switch (command) { + case Characteristic.VolumeSelector.INCREMENT: + command = this.masterVolume ? 'MVUP' : 'Z3UP'; + break; + case Characteristic.VolumeSelector.DECREMENT: + command = this.masterVolume ? 'MVDOWN' : 'Z3DOWN'; + break; + } - this.televisionService.getCharacteristic(Characteristic.ActiveIdentifier) - .onGet(async () => { - const inputIdentifier = this.inputIdentifier; - return inputIdentifier; - }) - .onSet(async (activeIdentifier) => { - try { - const index = this.inputsConfigured.findIndex(input => input.identifier === activeIdentifier); - const input = this.inputsConfigured[index]; - const inputName = input.name; - const inputMode = input.mode; - const inputReference = input.reference; - const reference = `${inputMode}${inputReference}`; - - switch (this.power) { - case false: - await new Promise(resolve => setTimeout(resolve, 4000)); - const tryAgain = this.power ? this.televisionService.setCharacteristic(Characteristic.ActiveIdentifier, activeIdentifier) : false; - break; - case true: - await this.denon.send(reference); - const info = this.disableLogInfo ? false : this.emit('message', `set Input Name: ${inputName}, Reference: ${reference}`); - break; - } - } catch (error) { - this.emit('error', `set Input error: ${error}`); - }; - }); + await this.denon.send(command); + const info = this.disableLogInfo ? false : this.emit('message', `set Volume Selector: ${command}`); + } catch (error) { + this.emit('error', `set Volume Selector error: ${error}`); + }; + }); + + this.speakerService.getCharacteristic(Characteristic.Volume) + .setProps({ + minValue: 0, + maxValue: this.volumeMax + }) + .onGet(async () => { + const volume = this.volume; + return volume; + }) + .onSet(async (value) => { + try { + value = value < 10 ? `0${value}` : value; + const volume = this.masterVolume ? `MV${value}` : `Z3${value}`; + await this.denon.send(volume); + const info = this.disableLogInfo ? false : this.emit('message', `set Volume: ${value - 80}`); + } catch (error) { + this.emit('error', `set Volume error: ${error}`); + }; + }); + + this.speakerService.getCharacteristic(Characteristic.Mute) + .onGet(async () => { + const state = this.mute; + return state; + }) + .onSet(async (state) => { + try { + const muteState = this.masterMute ? (state ? 'MUON' : 'MUOFF') : (state ? 'Z3MUON' : 'Z3MUOFF'); + await this.denon.send(muteState); + const info = this.disableLogInfo ? false : this.emit('message', `set Mute: ${state ? 'ON' : 'OFF'}`); + } catch (error) { + this.emit('error', `set Mute error: ${error}`); + }; + }); - this.televisionService.getCharacteristic(Characteristic.RemoteKey) - .onSet(async (command) => { - try { - const rcMedia = this.inputReference === 'SPOTIFY' || this.inputReference === 'BT' || this.inputReference === 'USB/IPOD' || this.inputReference === 'NET' || this.inputReference === 'MPLAY'; - switch (command) { - case Characteristic.RemoteKey.REWIND: - command = rcMedia ? 'NS9E' : 'MN9E'; - break; - case Characteristic.RemoteKey.FAST_FORWARD: - command = rcMedia ? 'NS9D' : 'MN9D'; - break; - case Characteristic.RemoteKey.NEXT_TRACK: - command = rcMedia ? 'MN9D' : 'MN9F'; - break; - case Characteristic.RemoteKey.PREVIOUS_TRACK: - command = rcMedia ? 'MN9E' : 'MN9G'; - break; - case Characteristic.RemoteKey.ARROW_UP: - command = rcMedia ? 'NS90' : 'MNCUP'; - break; - case Characteristic.RemoteKey.ARROW_DOWN: - command = rcMedia ? 'NS91' : 'MNCDN'; - break; - case Characteristic.RemoteKey.ARROW_LEFT: - command = rcMedia ? 'NS92' : 'MNCLT'; - break; - case Characteristic.RemoteKey.ARROW_RIGHT: - command = rcMedia ? 'NS93' : 'MNENT'; - break; - case Characteristic.RemoteKey.SELECT: - command = rcMedia ? 'NS94' : 'MNENT'; - break; - case Characteristic.RemoteKey.BACK: - command = rcMedia ? 'MNRTN' : 'MNRTN'; - break; - case Characteristic.RemoteKey.EXIT: - command = rcMedia ? 'MNRTN' : 'MNRTN'; - break; - case Characteristic.RemoteKey.PLAY_PAUSE: - command = rcMedia ? (this.mediaState ? 'NS9B' : 'NS9A') : 'NS94'; - this.mediaState = !this.mediaState; - break; - case Characteristic.RemoteKey.INFORMATION: - command = this.infoButtonCommand; - break; - } - - await this.denon.send(command); - const info = this.disableLogInfo ? false : this.emit('message', `set Remote Key: ${command}`); - } catch (error) { - this.emit('error', `set Remote Key error: ${error}`); - }; - }); - this.allServices.push(this.televisionService); + this.allServices.push(this.speakerService); - //prepare speaker service - const debug3 = !this.enableDebugMode ? false : this.emit('debug', `Prepare speaker service`); - this.speakerService = accessory.addService(Service.TelevisionSpeaker, `${accessoryName} Speaker`, 'Speaker'); - this.speakerService.getCharacteristic(Characteristic.Active) - .onGet(async () => { - const state = this.power; - return state; - }) - .onSet(async (state) => { - }); - this.speakerService.getCharacteristic(Characteristic.VolumeControlType) - .onGet(async () => { - const controlType = this.volumeControlType === 'Relative' ? 1 : 3; //none, relative, relative with current, absolute - const state = 3; - return state; - }) - this.speakerService.getCharacteristic(Characteristic.VolumeSelector) - .onSet(async (command) => { - try { - switch (command) { - case Characteristic.VolumeSelector.INCREMENT: - command = this.masterVolume ? 'MVUP' : 'Z3UP'; - break; - case Characteristic.VolumeSelector.DECREMENT: - command = this.masterVolume ? 'MVDOWN' : 'Z3DOWN'; - break; - } - - await this.denon.send(command); - const info = this.disableLogInfo ? false : this.emit('message', `set Volume Selector: ${command}`); - } catch (error) { - this.emit('error', `set Volume Selector error: ${error}`); - }; - }); + //prepare inputs service + const debug8 = !this.enableDebugMode ? false : this.emit('debug', `Prepare inputs services`); - this.speakerService.getCharacteristic(Characteristic.Volume) - .setProps({ - minValue: 0, - maxValue: this.volumeMax - }) + //check possible inputs count (max 85) + const inputs = allInputs; + const inputsCount = inputs.length; + const possibleInputsCount = 85 - this.allServices.length; + const maxInputsCount = inputsCount >= possibleInputsCount ? possibleInputsCount : inputsCount; + for (let i = 0; i < maxInputsCount; i++) { + //input + const input = inputs[i]; + const inputIdentifier = i + 1; + + //get input reference + const inputReference = input.reference; + + //get input name + const savedInputsName = this.savedInputsNames[inputReference] ?? false; + input.name = savedInputsName ? savedInputsName : input.name;; + + //get type + const inputSourceType = 0; + + //get configured + const isConfigured = 1; + + //get visibility + input.visibility = this.savedInputsTargetVisibility[inputReference] ?? 0; + + //add identifier to the input + input.identifier = inputIdentifier; + + //input service + const inputService = accessory.addService(Service.InputSource, input.name, `Input ${inputIdentifier}`); + inputService + .setCharacteristic(Characteristic.Identifier, inputIdentifier) + .setCharacteristic(Characteristic.Name, input.name) + .setCharacteristic(Characteristic.IsConfigured, isConfigured) + .setCharacteristic(Characteristic.InputSourceType, inputSourceType) + .setCharacteristic(Characteristic.CurrentVisibilityState, input.visibility) + + inputService.getCharacteristic(Characteristic.ConfiguredName) .onGet(async () => { - const volume = this.volume; - return volume; + return input.name; }) .onSet(async (value) => { + if (value === this.savedInputsNames[inputReference]) { + return; + } + try { - value = value < 10 ? `0${value}` : value; - const volume = this.masterVolume ? `MV${value}` : `Z3${value}`; - await this.denon.send(volume); - const info = this.disableLogInfo ? false : this.emit('message', `set Volume: ${value - 80}`); + input.name = value; + this.savedInputsNames[inputReference] = value; + await this.saveData(this.inputsNamesFile, this.savedInputsNames); + const debug = !this.enableDebugMode ? false : this.emit('debug', `Saved Input Name: ${value}, Reference: ${inputReference}`); + + //sort inputs + const index = this.inputsConfigured.findIndex(input => input.reference === inputReference); + this.inputsConfigured[index].name = value; + await this.displayOrder(); } catch (error) { - this.emit('error', `set Volume error: ${error}`); - }; + this.emit('error', `save Input Name error: ${error}`); + } }); - this.speakerService.getCharacteristic(Characteristic.Mute) + inputService.getCharacteristic(Characteristic.TargetVisibilityState) .onGet(async () => { - const state = this.mute; - return state; + return input.visibility; }) .onSet(async (state) => { + if (state === this.savedInputsTargetVisibility[inputReference]) { + return; + } + try { - const muteState = this.masterMute ? (state ? 'MUON' : 'MUOFF') : (state ? 'Z3MUON' : 'Z3MUOFF'); - await this.denon.send(muteState); - const info = this.disableLogInfo ? false : this.emit('message', `set Mute: ${state ? 'ON' : 'OFF'}`); + input.visibility = state; + this.savedInputsTargetVisibility[inputReference] = state; + await this.saveData(this.inputsTargetVisibilityFile, this.savedInputsTargetVisibility); + const debug = !this.enableDebugMode ? false : this.emit('debug', `Saved Input: ${input.name} Target Visibility: ${state ? 'HIDEN' : 'SHOWN'}`); } catch (error) { - this.emit('error', `set Mute error: ${error}`); - }; + this.emit('error', `save Input Target Visibility error: ${error}`); + } }); - this.allServices.push(this.speakerService); - - //prepare inputs service - const debug8 = !this.enableDebugMode ? false : this.emit('debug', `Prepare inputs services`); - - //check possible inputs count (max 85) - const inputs = allInputs; - const inputsCount = inputs.length; - const possibleInputsCount = 85 - this.allServices.length; - const maxInputsCount = inputsCount >= possibleInputsCount ? possibleInputsCount : inputsCount; - for (let i = 0; i < maxInputsCount; i++) { - //input - const input = inputs[i]; - const inputIdentifier = i + 1; - - //get input reference - const inputReference = input.reference; - - //get input name - const savedInputsName = this.savedInputsNames[inputReference] ?? false; - input.name = savedInputsName ? savedInputsName : input.name;; - - //get type - const inputSourceType = 0; - - //get configured - const isConfigured = 1; - - //get visibility - input.visibility = this.savedInputsTargetVisibility[inputReference] ?? 0; - - //add identifier to the input - input.identifier = inputIdentifier; - - //input service - const inputService = accessory.addService(Service.InputSource, input.name, `Input ${inputIdentifier}`); - inputService - .setCharacteristic(Characteristic.Identifier, inputIdentifier) - .setCharacteristic(Characteristic.Name, input.name) - .setCharacteristic(Characteristic.IsConfigured, isConfigured) - .setCharacteristic(Characteristic.InputSourceType, inputSourceType) - .setCharacteristic(Characteristic.CurrentVisibilityState, input.visibility) + this.inputsConfigured.push(input); + this.televisionService.addLinkedService(inputService); + this.allServices.push(inputService); + }; - inputService.getCharacteristic(Characteristic.ConfiguredName) + //prepare volume service + if (this.volumeControl) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare volume service`); + const volumeServiceName = this.volumeControlNamePrefix ? `${accessoryName} ${this.volumeControlName}` : this.volumeControlName; + if (this.volumeControl === 1) { + this.volumeService = accessory.addService(Service.Lightbulb, `${volumeServiceName}`, 'Volume'); + this.volumeService.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.volumeService.setCharacteristic(Characteristic.ConfiguredName, `${volumeServiceName}`); + this.volumeService.getCharacteristic(Characteristic.Brightness) + .setProps({ + minValue: 0, + maxValue: this.volumeMax + }) .onGet(async () => { - return input.name; + const volume = this.volume; + return volume; }) - .onSet(async (value) => { - if (value === this.savedInputsNames[inputReference]) { - return; - } - - try { - input.name = value; - this.savedInputsNames[inputReference] = value; - await this.saveData(this.inputsNamesFile, this.savedInputsNames); - const debug = !this.enableDebugMode ? false : this.emit('debug', `Saved Input Name: ${value}, Reference: ${inputReference}`); - - //sort inputs - const index = this.inputsConfigured.findIndex(input => input.reference === inputReference); - this.inputsConfigured[index].name = value; - await this.displayOrder(); - } catch (error) { - this.emit('error', `save Input Name error: ${error}`); - } + .onSet(async (volume) => { + this.speakerService.setCharacteristic(Characteristic.Volume, volume); }); - - inputService.getCharacteristic(Characteristic.TargetVisibilityState) + this.volumeService.getCharacteristic(Characteristic.On) .onGet(async () => { - return input.visibility; + const state = !this.mute; + return state; }) .onSet(async (state) => { - if (state === this.savedInputsTargetVisibility[inputReference]) { - return; - } - - try { - input.visibility = state; - this.savedInputsTargetVisibility[inputReference] = state; - await this.saveData(this.inputsTargetVisibilityFile, this.savedInputsTargetVisibility); - const debug = !this.enableDebugMode ? false : this.emit('debug', `Saved Input: ${input.name} Target Visibility: ${state ? 'HIDEN' : 'SHOWN'}`); - } catch (error) { - this.emit('error', `save Input Target Visibility error: ${error}`); - } + this.speakerService.setCharacteristic(Characteristic.Mute, !state); }); - this.inputsConfigured.push(input); - this.televisionService.addLinkedService(inputService); - this.allServices.push(inputService); - }; - - //prepare volume service - if (this.volumeControl) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare volume service`); - const volumeServiceName = this.volumeControlNamePrefix ? `${accessoryName} ${this.volumeControlName}` : this.volumeControlName; - if (this.volumeControl === 1) { - this.volumeService = accessory.addService(Service.Lightbulb, `${volumeServiceName}`, 'Volume'); - this.volumeService.addOptionalCharacteristic(Characteristic.ConfiguredName); - this.volumeService.setCharacteristic(Characteristic.ConfiguredName, `${volumeServiceName}`); - this.volumeService.getCharacteristic(Characteristic.Brightness) - .setProps({ - minValue: 0, - maxValue: this.volumeMax - }) - .onGet(async () => { - const volume = this.volume; - return volume; - }) - .onSet(async (volume) => { - this.speakerService.setCharacteristic(Characteristic.Volume, volume); - }); - this.volumeService.getCharacteristic(Characteristic.On) - .onGet(async () => { - const state = !this.mute; - return state; - }) - .onSet(async (state) => { - this.speakerService.setCharacteristic(Characteristic.Mute, !state); - }); - - this.allServices.push(this.volumeService); - } - - if (this.volumeControl === 2) { - this.volumeServiceFan = accessory.addService(Service.Fan, `${volumeServiceName}`, 'Volume'); - this.volumeServiceFan.addOptionalCharacteristic(Characteristic.ConfiguredName); - this.volumeServiceFan.setCharacteristic(Characteristic.ConfiguredName, `${volumeServiceName}`); - this.volumeServiceFan.getCharacteristic(Characteristic.RotationSpeed) - .setProps({ - minValue: 0, - maxValue: this.volumeMax - }) - .onGet(async () => { - const volume = this.volume; - return volume; - }) - .onSet(async (volume) => { - this.speakerService.setCharacteristic(Characteristic.Volume, volume); - }); - this.volumeServiceFan.getCharacteristic(Characteristic.On) - .onGet(async () => { - const state = !this.mute; - return state; - }) - .onSet(async (state) => { - this.speakerService.setCharacteristic(Characteristic.Mute, !state); - }); - - this.allServices.push(this.volumeServiceFan); - } - }; + this.allServices.push(this.volumeService); + } - //prepare sensor service - if (this.sensorPower) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare power sensor service`); - this.sensorPowerService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Power Sensor`, `Power Sensor`); - this.sensorPowerService.addOptionalCharacteristic(Characteristic.ConfiguredName); - this.sensorPowerService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Power Sensor`); - this.sensorPowerService.getCharacteristic(Characteristic.ContactSensorState) + if (this.volumeControl === 2) { + this.volumeServiceFan = accessory.addService(Service.Fan, `${volumeServiceName}`, 'Volume'); + this.volumeServiceFan.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.volumeServiceFan.setCharacteristic(Characteristic.ConfiguredName, `${volumeServiceName}`); + this.volumeServiceFan.getCharacteristic(Characteristic.RotationSpeed) + .setProps({ + minValue: 0, + maxValue: this.volumeMax + }) .onGet(async () => { - const state = this.power; - return state; + const volume = this.volume; + return volume; + }) + .onSet(async (volume) => { + this.speakerService.setCharacteristic(Characteristic.Volume, volume); }); - - this.allServices.push(this.sensorPowerService); - }; - - if (this.sensorVolume) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare volume sensor service`); - this.sensorVolumeService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Volume Sensor`, `Volume Sensor`); - this.sensorVolumeService.addOptionalCharacteristic(Characteristic.ConfiguredName); - this.sensorVolumeService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Volume Sensor`); - this.sensorVolumeService.getCharacteristic(Characteristic.ContactSensorState) + this.volumeServiceFan.getCharacteristic(Characteristic.On) .onGet(async () => { - const state = this.sensorVolumeState; + const state = !this.mute; return state; + }) + .onSet(async (state) => { + this.speakerService.setCharacteristic(Characteristic.Mute, !state); }); - this.allServices.push(this.sensorVolumeService); - }; + this.allServices.push(this.volumeServiceFan); + } + }; - if (this.sensorMute) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare mute sensor service`); - this.sensorMuteService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Mute Sensor`, `Mute Sensor`); - this.sensorMuteService.addOptionalCharacteristic(Characteristic.ConfiguredName); - this.sensorMuteService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Mute Sensor`); - this.sensorMuteService.getCharacteristic(Characteristic.ContactSensorState) - .onGet(async () => { - const state = this.power ? this.mute : false; - return state; - }); + //prepare sensor service + if (this.sensorPower) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare power sensor service`); + this.sensorPowerService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Power Sensor`, `Power Sensor`); + this.sensorPowerService.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.sensorPowerService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Power Sensor`); + this.sensorPowerService.getCharacteristic(Characteristic.ContactSensorState) + .onGet(async () => { + const state = this.power; + return state; + }); - this.allServices.push(this.sensorMuteService); - }; + this.allServices.push(this.sensorPowerService); + }; - if (this.sensorInput) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare input sensor service`); - this.sensorInputService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Input Sensor`, `Input Sensor`); - this.sensorInputService.addOptionalCharacteristic(Characteristic.ConfiguredName); - this.sensorInputService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Input Sensor`); - this.sensorInputService.getCharacteristic(Characteristic.ContactSensorState) - .onGet(async () => { - const state = this.sensorInputState; - return state; - }); + if (this.sensorVolume) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare volume sensor service`); + this.sensorVolumeService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Volume Sensor`, `Volume Sensor`); + this.sensorVolumeService.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.sensorVolumeService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Volume Sensor`); + this.sensorVolumeService.getCharacteristic(Characteristic.ContactSensorState) + .onGet(async () => { + const state = this.sensorVolumeState; + return state; + }); - this.allServices.push(this.sensorInputService); - }; + this.allServices.push(this.sensorVolumeService); + }; - //prepare sonsor service - const possibleSensorInputsCount = 99 - this.allServices.length; - const maxSensorInputsCount = this.sensorsInputsConfiguredCount >= possibleSensorInputsCount ? possibleSensorInputsCount : this.sensorsInputsConfiguredCount; - if (maxSensorInputsCount > 0) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare inputs sensors services`); - for (let i = 0; i < maxSensorInputsCount; i++) { - //get sensor - const sensorInput = this.sensorsInputsConfigured[i]; + if (this.sensorMute) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare mute sensor service`); + this.sensorMuteService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Mute Sensor`, `Mute Sensor`); + this.sensorMuteService.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.sensorMuteService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Mute Sensor`); + this.sensorMuteService.getCharacteristic(Characteristic.ContactSensorState) + .onGet(async () => { + const state = this.power ? this.mute : false; + return state; + }); + + this.allServices.push(this.sensorMuteService); + }; + + if (this.sensorInput) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare input sensor service`); + this.sensorInputService = accessory.addService(Service.ContactSensor, `${this.sZoneName} Input Sensor`, `Input Sensor`); + this.sensorInputService.addOptionalCharacteristic(Characteristic.ConfiguredName); + this.sensorInputService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Input Sensor`); + this.sensorInputService.getCharacteristic(Characteristic.ContactSensorState) + .onGet(async () => { + const state = this.sensorInputState; + return state; + }); - //get sensor name - const sensorInputName = sensorInput.name; + this.allServices.push(this.sensorInputService); + }; - //get sensor name prefix - const namePrefix = sensorInput.namePrefix || false; + //prepare sonsor service + const possibleSensorInputsCount = 99 - this.allServices.length; + const maxSensorInputsCount = this.sensorsInputsConfiguredCount >= possibleSensorInputsCount ? possibleSensorInputsCount : this.sensorsInputsConfiguredCount; + if (maxSensorInputsCount > 0) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare inputs sensors services`); + for (let i = 0; i < maxSensorInputsCount; i++) { + //get sensor + const sensorInput = this.sensorsInputsConfigured[i]; - //get service type - const serviceType = sensorInput.serviceType; + //get sensor name + const sensorInputName = sensorInput.name; - //get service type - const characteristicType = sensorInput.characteristicType; + //get sensor name prefix + const namePrefix = sensorInput.namePrefix || false; - const serviceName = namePrefix ? `${accessoryName} ${sensorInputName}` : sensorInputName; - const sensorInputService = new serviceType(serviceName, `Sensor ${i}`); - sensorInputService.addOptionalCharacteristic(Characteristic.ConfiguredName); - sensorInputService.setCharacteristic(Characteristic.ConfiguredName, serviceName); - sensorInputService.getCharacteristic(characteristicType) - .onGet(async () => { - const state = sensorInput.state - return state; - }); - this.sensorsInputsServices.push(sensorInputService); - this.allServices.push(sensorInputService); - accessory.addService(sensorInputService); - } + //get service type + const serviceType = sensorInput.serviceType; + + //get service type + const characteristicType = sensorInput.characteristicType; + + const serviceName = namePrefix ? `${accessoryName} ${sensorInputName}` : sensorInputName; + const sensorInputService = new serviceType(serviceName, `Sensor ${i}`); + sensorInputService.addOptionalCharacteristic(Characteristic.ConfiguredName); + sensorInputService.setCharacteristic(Characteristic.ConfiguredName, serviceName); + sensorInputService.getCharacteristic(characteristicType) + .onGet(async () => { + const state = sensorInput.state + return state; + }); + this.sensorsInputsServices.push(sensorInputService); + this.allServices.push(sensorInputService); + accessory.addService(sensorInputService); } + } - //prepare buttons services - const possibleButtonsCount = 99 - this.allServices.length; - const maxButtonsCount = this.buttonsConfiguredCount >= possibleButtonsCount ? possibleButtonsCount : this.buttonsConfiguredCount; - if (maxButtonsCount > 0) { - const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare buttons services`); - for (let i = 0; i < maxButtonsCount; i++) { - //get button - const button = this.buttonsConfigured[i]; + //prepare buttons services + const possibleButtonsCount = 99 - this.allServices.length; + const maxButtonsCount = this.buttonsConfiguredCount >= possibleButtonsCount ? possibleButtonsCount : this.buttonsConfiguredCount; + if (maxButtonsCount > 0) { + const debug = !this.enableDebugMode ? false : this.emit('debug', `Prepare buttons services`); + for (let i = 0; i < maxButtonsCount; i++) { + //get button + const button = this.buttonsConfigured[i]; - //get button name - const buttonName = button.name; - - //get button reference - const buttonReference = button.reference; - - //get button name prefix - const namePrefix = button.namePrefix || false; - - //get service type - const serviceType = button.serviceType; - - const serviceName = namePrefix ? `${accessoryName} ${buttonName}` : buttonName; - const buttonService = new serviceType(serviceName, `Button ${i}`); - buttonService.addOptionalCharacteristic(Characteristic.ConfiguredName); - buttonService.setCharacteristic(Characteristic.ConfiguredName, serviceName); - buttonService.getCharacteristic(Characteristic.On) - .onGet(async () => { - const state = button.state; - return state; - }) - .onSet(async (state) => { - try { - const reference = `Z3${buttonReference.substring(1)}`; - const set = state ? await this.denon.send(reference) : false; - const info = this.disableLogInfo || !state ? false : this.emit('message', `set Button Name: ${buttonName}, Reference: ${reference}`); - } catch (error) { - this.emit('error', `set Button error: ${error}`); - }; - }); + //get button name + const buttonName = button.name; - this.buttonsServices.push(buttonService); - this.allServices.push(buttonService); - accessory.addService(buttonService); - }; - }; + //get button reference + const buttonReference = button.reference; - resolve(accessory); - } catch (error) { - reject(error) + //get button name prefix + const namePrefix = button.namePrefix || false; + + //get service type + const serviceType = button.serviceType; + + const serviceName = namePrefix ? `${accessoryName} ${buttonName}` : buttonName; + const buttonService = new serviceType(serviceName, `Button ${i}`); + buttonService.addOptionalCharacteristic(Characteristic.ConfiguredName); + buttonService.setCharacteristic(Characteristic.ConfiguredName, serviceName); + buttonService.getCharacteristic(Characteristic.On) + .onGet(async () => { + const state = button.state; + return state; + }) + .onSet(async (state) => { + try { + const reference = `Z3${buttonReference.substring(1)}`; + const set = state ? await this.denon.send(reference) : false; + const info = this.disableLogInfo || !state ? false : this.emit('message', `set Button Name: ${buttonName}, Reference: ${reference}`); + } catch (error) { + this.emit('error', `set Button error: ${error}`); + }; + }); + + this.buttonsServices.push(buttonService); + this.allServices.push(buttonService); + accessory.addService(buttonService); + }; }; - }); + + return accessory; + } catch (error) { + this.emitDeviceInfo('error', error) + }; } };