homebridge-denon-heos-audio
Version:
Control your Denon speakers via AVR Control and/or HeosCLI
595 lines • 29.9 kB
JavaScript
import { DOMParser } from "@xmldom/xmldom";
import { PromiseTimeoutException } from "./promiseTimeoutException.js";
import { Playing, RaceStatus } from "./denonClient.js";
import * as DenonProtocol from "./denonProtocol.js";
import * as CustomLogging from "./customLogging.js";
/**
* Platform Accessory
* An instance of this class is created for each accessory your platform registers
* Each accessory may expose multiple services of different service types.
*/
export class DenonAudioAccessory {
static CALLBACK_TIMEOUT = 1500;
static API_CONNECT_TIMEOUT = 5 * 1000;
static API_RESPONSE_TIMEOUT = 1 * 1000;
platform;
accessory;
denonClient;
informationService;
tvService;
speakerService;
inputServices = [];
rawLog;
log;
name;
ip;
serialNumber;
controlMode;
volumeStepSize;
volumeLimit;
configuredInputsByIdentifier = new Map();
configuredInputsByInputId = new Map();
unknownInput;
lastSetVolume;
targetMediaState;
volumeChangeOngoing = false;
playingChangeOngoing = false;
constructor(platform, accessory, config, log) {
log.debug("Initializing DenonAudioAccessory...");
this.platform = platform;
this.accessory = accessory;
this.rawLog = log;
this.log = new CustomLogging.LoggerPrefixWrapper(this.rawLog, accessory.displayName);
this.name = accessory.displayName;
this.ip = config.ip;
this.serialNumber = config.serialNumber;
this.controlMode = DenonProtocol.ControlMode[config.controlProtocol];
if (config.volumeLimitEnabled && (config.volumeLimit < 0 || config.volumeLimit > 99)) {
throw new Error("Volume limit must be between 0 and 99");
}
this.volumeLimit = config.volumeLimitEnabled ? config.volumeLimit : undefined;
if (config.volumeStepSize < 1 || config.volumeStepSize > 10) {
throw new Error("Volume step size must be between 1 and 10");
}
this.volumeStepSize = config.volumeStepSize;
// set accessory category
accessory.category = 31 /* this.platform.api.hap.Categories.TELEVISION */;
// add information service
this.informationService = this.accessory.getService(this.platform.Service.AccessoryInformation);
this.informationService.getCharacteristic(this.platform.Characteristic.Identify).onSet(this.setIdentify.bind(this));
this.informationService.setCharacteristic(this.platform.Characteristic.Name, this.name);
this.informationService.setCharacteristic(this.platform.Characteristic.SerialNumber, this.serialNumber);
this.informationService.setCharacteristic(this.platform.Characteristic.Manufacturer, "unknown");
this.informationService.setCharacteristic(this.platform.Characteristic.Model, "unknown");
this.informationService.setCharacteristic(this.platform.Characteristic.FirmwareRevision, "unknown");
this.fetchMetadataAios();
// add tv service
this.tvService = this.accessory.getService(`${this.name} TV`) || this.accessory.addService(this.platform.Service.Television, `${this.name} TV`);
this.tvService.setCharacteristic(this.platform.Characteristic.ConfiguredName, this.name);
this.tvService.setCharacteristic(this.platform.Characteristic.SleepDiscoveryMode, this.platform.Characteristic.SleepDiscoveryMode.ALWAYS_DISCOVERABLE);
this.tvService.getCharacteristic(this.platform.Characteristic.Active).onGet(this.getActive.bind(this)).onSet(this.setActive.bind(this));
this.tvService
.getCharacteristic(this.platform.Characteristic.ActiveIdentifier)
.onGet(this.getActiveIdentifier.bind(this))
.onSet(this.setActiveIdentifier.bind(this));
if (this.controlMode !== DenonProtocol.ControlMode.AVRCONTROL) {
this.tvService.getCharacteristic(this.platform.Characteristic.CurrentMediaState).onGet(this.getCurrentMediaState.bind(this));
this.tvService
.getCharacteristic(this.platform.Characteristic.TargetMediaState)
.onGet(this.getTargetMediaState.bind(this))
.onSet(this.setTargetMediaState.bind(this));
}
this.tvService.getCharacteristic(this.platform.Characteristic.RemoteKey).onSet(this.setRemoteKey.bind(this));
// add speaker service
this.speakerService =
this.accessory.getService(`${this.name} Speaker`) || this.accessory.addService(this.platform.Service.TelevisionSpeaker, `${this.name} Speaker`);
this.speakerService.getCharacteristic(this.platform.Characteristic.Mute).onGet(this.getMute.bind(this)).onSet(this.setMute.bind(this));
this.speakerService.getCharacteristic(this.platform.Characteristic.Volume).onGet(this.getVolume.bind(this)).onSet(this.setVolume.bind(this));
this.speakerService.getCharacteristic(this.platform.Characteristic.VolumeSelector).onSet(this.setVolumeSelector.bind(this));
this.speakerService.setCharacteristic(this.platform.Characteristic.VolumeControlType, this.platform.Characteristic.VolumeControlType.ABSOLUTE);
// choose appropriate client
this.denonClient = new DenonProtocol.CLIENT_MAP[this.controlMode](this.serialNumber, this.ip, DenonAudioAccessory.API_CONNECT_TIMEOUT, DenonAudioAccessory.API_RESPONSE_TIMEOUT, this.log.debug.bind(this.log), this.callbackActive.bind(this), this.callbackMute.bind(this), this.callbackVolume.bind(this), this.callbackInput.bind(this));
// specify inputs to add
let i = 1;
if (config.inputs) {
// user-specified inputs
for (const config_input of config.inputs) {
if (this.configuredInputsByInputId.has(config_input.inputID)) {
log.warn(`Duplicate input ID found in configuration: ${config_input.inputID}. Skipping this input.`);
continue;
}
const configured_input = new ConfiguredInput(config_input.inputID, config_input.name, config_input.showInList, i, true);
this.configuredInputsByInputId.set(configured_input.inputID, configured_input);
this.configuredInputsByIdentifier.set(i, configured_input);
i++;
}
}
for (const default_input of this.denonClient.defaultInputs) {
if (this.configuredInputsByInputId.has(default_input.inputID)) {
// this input id is already configured by the user, skipping
continue;
}
const configured_input = new ConfiguredInput(default_input.inputID, default_input.displayName, false, i, false);
this.configuredInputsByInputId.set(configured_input.inputID, configured_input);
this.configuredInputsByIdentifier.set(i, configured_input);
i++;
}
this.unknownInput = new ConfiguredInput("unknown", "Unknown Input", false, i, false);
this.configuredInputsByInputId.set(this.unknownInput.inputID, this.unknownInput);
this.configuredInputsByIdentifier.set(i, this.unknownInput);
for (const configured_input of this.configuredInputsByIdentifier.values()) {
const inputService = this.accessory.getService(`${this.name} Input ${configured_input.inputID}`) ||
this.accessory.addService(this.platform.Service.InputSource, `${this.name} Input ${configured_input.inputID}`, configured_input.inputID);
inputService.setCharacteristic(this.platform.Characteristic.Identifier, configured_input.identifier);
inputService.setCharacteristic(this.platform.Characteristic.ConfiguredName, configured_input.displayName);
inputService.setCharacteristic(this.platform.Characteristic.InputSourceType, this.platform.Characteristic.InputSourceType.OTHER);
inputService.setCharacteristic(this.platform.Characteristic.IsConfigured, this.platform.Characteristic.IsConfigured.CONFIGURED);
inputService.setCharacteristic(this.platform.Characteristic.CurrentVisibilityState, configured_input.showInList ? this.platform.Characteristic.CurrentVisibilityState.SHOWN : this.platform.Characteristic.CurrentVisibilityState.HIDDEN);
this.tvService.addLinkedService(inputService);
this.inputServices.push(inputService);
log.debug(`Added ${configured_input.userDefined ? "user-defined" : "default"} input service for input ID ${configured_input.inputID} with identifier ${configured_input.identifier}.`);
}
this.log.info("Finished initializing accessory.");
}
fetchMetadataAios() {
fetch(`http://${this.ip}:60006/upnp/desc/aios_device/aios_device.xml`)
.then((response) => {
if (!response.ok) {
this.log.debug(`Received a non-200 status code while connecting to a new device's location href (status code: ${response.status}).`);
}
response.text().then((text) => {
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(text, "text/xml");
this.informationService.setCharacteristic(this.platform.Characteristic.Manufacturer, xmlDoc.getElementsByTagName("device")[0]?.getElementsByTagName("manufacturer")[0]?.textContent || "unknown");
this.informationService.setCharacteristic(this.platform.Characteristic.Model, xmlDoc.getElementsByTagName("device")[0]?.getElementsByTagName("modelName")[0]?.textContent || "unknown");
const d_list = xmlDoc.getElementsByTagName("device")[0]?.getElementsByTagName("deviceList")[0]?.getElementsByTagName("device") || [];
for (const d of d_list) {
const firmware_version = d.getElementsByTagName("firmware_version")[0]?.textContent;
if (firmware_version) {
this.informationService.setCharacteristic(this.platform.Characteristic.FirmwareRevision, firmware_version || "unknown");
break;
}
}
});
})
.catch(() => {
this.log.warn(`An error occured while connecting to ${this.name}'s location href.`);
});
}
/**
* Handle the "GET" requests from HomeKit
* These are sent when HomeKit wants to know the current state of the accessory, for example, checking if a Light bulb is on.
*/
async getActive() {
const raceStatus = new RaceStatus();
this.log.debug(`getPower for ${this.name}. [race id: ${raceStatus.raceId}]`);
try {
return await Promise.race([
this.denonClient.getPower(raceStatus),
new Promise((resolve, reject) => {
setTimeout(() => {
raceStatus.setRaceOver();
reject(new PromiseTimeoutException(DenonAudioAccessory.CALLBACK_TIMEOUT));
}, DenonAudioAccessory.CALLBACK_TIMEOUT);
}),
]);
}
catch (error) {
if (error instanceof PromiseTimeoutException) {
this.log.debug(`${this.name} lost its promise race for getActive(). [race id: ${raceStatus.raceId}]`);
}
else {
this.log.error(`An error occured while getting power status for ${this.name}. [race id: ${raceStatus.raceId}]`, error);
}
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
}
}
async getActiveIdentifier() {
const raceStatus = new RaceStatus();
this.log.debug(`getActiveIdentifier for ${this.name}. [race id: ${raceStatus.raceId}]`);
try {
const current_input = await Promise.race([
this.denonClient.getInput(raceStatus),
new Promise((resolve, reject) => {
setTimeout(() => {
raceStatus.setRaceOver();
reject(new PromiseTimeoutException(DenonAudioAccessory.CALLBACK_TIMEOUT));
}, DenonAudioAccessory.CALLBACK_TIMEOUT);
}),
]);
return this.configuredInputsByInputId.get(current_input)?.identifier || this.unknownInput.identifier;
}
catch (error) {
if (error instanceof PromiseTimeoutException) {
this.log.debug(`${this.name} lost its promise race for getActiveIdentifier(). [race id: ${raceStatus.raceId}]`);
}
else {
this.log.error(`An error occured while getting current input for ${this.name}. [race id: ${raceStatus.raceId}]`, error);
}
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
}
}
async getPlaying(raceStatus) {
try {
return await Promise.race([
this.denonClient.getPlaying(raceStatus),
new Promise((resolve, reject) => {
setTimeout(() => {
raceStatus.setRaceOver();
reject(new PromiseTimeoutException(DenonAudioAccessory.CALLBACK_TIMEOUT));
}, DenonAudioAccessory.CALLBACK_TIMEOUT);
}),
]);
}
catch (error) {
if (error instanceof PromiseTimeoutException) {
this.log.debug(`${this.name} lost its promise race for getPlaying(). [race id: ${raceStatus.raceId}]`);
}
else {
this.log.error(`An error occured while getting playing status for ${this.name}. [race id: ${raceStatus.raceId}]`, error);
}
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
}
}
async getCurrentMediaState() {
const raceStatus = new RaceStatus();
this.log.debug(`getCurrentMediaState for ${this.name}. [race id: ${raceStatus.raceId}]`);
const playing = await this.getPlaying(raceStatus);
switch (playing) {
case Playing.PLAY:
return this.platform.Characteristic.CurrentMediaState.PLAY;
case Playing.PAUSE:
return this.platform.Characteristic.CurrentMediaState.PAUSE;
case Playing.STOP:
case Playing.UNSUPPORTED:
return this.platform.Characteristic.CurrentMediaState.STOP;
}
}
async getTargetMediaState() {
if (this.targetMediaState) {
this.log.debug(`getTargetMediaState for ${this.name}. [retrieving from cache]`);
return this.targetMediaState;
}
else {
const raceStatus = new RaceStatus();
this.log.debug(`getTargetMediaState for ${this.name}. [race id: ${raceStatus.raceId}]`);
const playing = await this.getPlaying(raceStatus);
switch (playing) {
case Playing.PLAY:
return this.platform.Characteristic.TargetMediaState.PLAY;
case Playing.PAUSE:
return this.platform.Characteristic.TargetMediaState.PAUSE;
case Playing.STOP:
case Playing.UNSUPPORTED:
return this.platform.Characteristic.TargetMediaState.STOP;
}
}
}
async getMute() {
const raceStatus = new RaceStatus();
this.log.debug(`getMute for ${this.name}. [race id: ${raceStatus.raceId}]`);
try {
return await Promise.race([
this.denonClient.getMute(raceStatus),
new Promise((resolve, reject) => {
setTimeout(() => {
raceStatus.setRaceOver();
reject(new PromiseTimeoutException(DenonAudioAccessory.CALLBACK_TIMEOUT));
}, DenonAudioAccessory.CALLBACK_TIMEOUT);
}),
]);
}
catch (error) {
if (error instanceof PromiseTimeoutException) {
this.log.debug(`${this.name} lost its promise race for getMute(). [race id: ${raceStatus.raceId}]`);
}
else {
this.log.error(`An error occured while getting mute status for ${this.name}. [race id: ${raceStatus.raceId}]`, error);
}
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
}
}
async getVolume() {
const raceStatus = new RaceStatus();
this.log.debug(`getVolume for ${this.name}. [race id: ${raceStatus.raceId}]`);
try {
const volume = await Promise.race([
this.denonClient.getVolume(raceStatus),
new Promise((resolve, reject) => {
setTimeout(() => {
raceStatus.setRaceOver();
reject(new PromiseTimeoutException(DenonAudioAccessory.CALLBACK_TIMEOUT));
}, DenonAudioAccessory.CALLBACK_TIMEOUT);
}),
]);
return this.adjustBackFromVolumeLimit(volume);
}
catch (error) {
if (error instanceof PromiseTimeoutException) {
this.log.debug(`${this.name} lost its promise race for getVolume(). [race id: ${raceStatus.raceId}]`);
}
else {
this.log.error(`An error occured while getting volume for ${this.name}. [race id: ${raceStatus.raceId}]`, error);
}
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
}
}
/*
* Handle "SET" requests from HomeKit
* These are sent when the user changes the state of an accessory, for example, turning on a Light bulb.
*/
setIdentify(value) {
this.log.info("Triggered SET Identify:", value);
}
setActive(newValue) {
this.log.debug(`setActive for ${this.name} set to ${newValue}`);
this.denonClient.setPower(newValue === this.platform.Characteristic.Active.ACTIVE).catch((error) => {
this.log.error(`An error occured while setting power status for ${this.name}.`, error);
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
});
}
setActiveIdentifier(newValue) {
this.log.debug(`setActiveIdentifier for ${this.name} set to ${newValue}`);
if (!this.configuredInputsByIdentifier.has(newValue)) {
this.log.error(`An error occured while setting input for ${this.name}. Input identifier ${newValue} is not configured.`);
throw new this.platform.api.hap.HapStatusError(-70412 /* this.platform.api.hap.HAPStatus.NOT_ALLOWED_IN_CURRENT_STATE */);
}
this.denonClient.setInput(this.configuredInputsByIdentifier.get(newValue).inputID).catch((error) => {
this.log.error(`An error occured while setting input for ${this.name}.`, error);
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
});
}
setRemoteKey(remoteKey) {
switch (remoteKey) {
case this.platform.Characteristic.RemoteKey.REWIND: {
// unsupported in iOS
break;
}
case this.platform.Characteristic.RemoteKey.FAST_FORWARD: {
// unsupported in iOS
break;
}
case this.platform.Characteristic.RemoteKey.NEXT_TRACK: {
// unsupported in iOS
this.setPlayNext();
break;
}
case this.platform.Characteristic.RemoteKey.PREVIOUS_TRACK: {
// unsupported in iOS
this.setPlayPrevious();
break;
}
case this.platform.Characteristic.RemoteKey.ARROW_UP: {
break;
}
case this.platform.Characteristic.RemoteKey.ARROW_DOWN: {
break;
}
case this.platform.Characteristic.RemoteKey.ARROW_LEFT: {
this.setPlayPrevious();
break;
}
case this.platform.Characteristic.RemoteKey.ARROW_RIGHT: {
this.setPlayNext();
break;
}
case this.platform.Characteristic.RemoteKey.SELECT: {
this.setPlayPauseToggle();
break;
}
case this.platform.Characteristic.RemoteKey.BACK: {
// unsupported in iOS
break;
}
case this.platform.Characteristic.RemoteKey.EXIT: {
// unsupported in iOS
break;
}
case this.platform.Characteristic.RemoteKey.PLAY_PAUSE: {
this.setPlayPauseToggle();
break;
}
case this.platform.Characteristic.RemoteKey.INFORMATION: {
break;
}
}
}
async setTargetMediaState(newValue) {
this.log.debug(`setTargetMediaState for ${this.name} set to ${newValue}`);
if (this.playingChangeOngoing) {
this.log.debug(`setPlayPauseToggle for ${this.name} was called while another play state change is still ongoing. Ignoring this call.`);
return;
}
this.playingChangeOngoing = true;
switch (newValue) {
case this.platform.Characteristic.TargetMediaState.PLAY:
this.targetMediaState = Playing.PLAY;
break;
case this.platform.Characteristic.TargetMediaState.PAUSE:
this.targetMediaState = Playing.PAUSE;
break;
case this.platform.Characteristic.TargetMediaState.STOP:
this.targetMediaState = Playing.STOP;
break;
default:
throw new Error(`Unexpected target media state: ${newValue}`);
}
try {
await this.denonClient.setPlaying(this.targetMediaState);
}
catch (error) {
this.log.error(`An error occured while setting mute status for ${this.name}.`, error);
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
}
finally {
this.targetMediaState = undefined;
this.playingChangeOngoing = false;
}
}
async setPlayPauseToggle() {
const raceStatus = new RaceStatus();
this.log.debug(`setPlayPauseToggle for ${this.name} [race id: ${raceStatus.raceId}]`);
if (this.playingChangeOngoing) {
this.log.debug(`setPlayPauseToggle for ${this.name} was called while another play state change is still ongoing. Ignoring this call.`);
return;
}
try {
this.playingChangeOngoing = true;
const currentPlaying = await this.getPlaying(raceStatus);
switch (currentPlaying) {
case Playing.PLAY:
this.targetMediaState = Playing.PAUSE;
break;
case Playing.PAUSE:
case Playing.STOP:
this.targetMediaState = Playing.PLAY;
break;
case Playing.UNSUPPORTED:
return;
}
await this.denonClient.setPlaying(this.targetMediaState);
}
catch (error) {
this.log.error(`An error occured while toggling play/pause for ${this.name}.`, error);
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
}
finally {
this.targetMediaState = undefined;
this.playingChangeOngoing = false;
}
}
setPlayNext() {
this.log.debug(`setPlayNext for ${this.name}`);
this.denonClient.setPlayNext().catch((error) => {
this.log.error(`An error occured while setting play next for ${this.name}.`, error);
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
});
}
setPlayPrevious() {
this.log.debug(`setPlayPrevious for ${this.name}`);
this.denonClient.setPlayPrevious().catch((error) => {
this.log.error(`An error occured while setting play previous for ${this.name}.`, error);
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
});
}
setMute(newValue) {
this.log.debug(`setMute for ${this.name} set to ${newValue}`);
this.denonClient.setMute(newValue).catch((error) => {
this.log.error(`An error occured while setting mute status for ${this.name}.`, error);
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
});
}
async setVolume(newValue) {
this.log.debug(`setVolume for ${this.name} set to ${newValue}`);
if (this.volumeChangeOngoing) {
this.log.debug(`setVolume for ${this.name} was called while another volume change is still ongoing. Ignoring this call.`);
return;
}
try {
this.volumeChangeOngoing = true;
this.log.debug(`setVolume for ${this.name} set to ${newValue}`);
this.lastSetVolume = newValue;
const volumetoSet = this.adjustToVolumeLimit(newValue);
this.denonClient.setVolume(volumetoSet);
}
catch (error) {
this.log.error(`An error occured while setting volume for ${this.name}.`, error);
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
}
finally {
this.volumeChangeOngoing = false;
}
}
async setVolumeSelector(direction) {
if (this.volumeChangeOngoing) {
this.log.debug(`setVolumeSelector for ${this.name} was called while another volume change is still ongoing. Ignoring this call.`);
return;
}
try {
this.volumeChangeOngoing = true;
this.log.debug(`setVolumeSelector for ${this.name} set to ${direction}`);
if (direction === this.platform.Characteristic.VolumeSelector.INCREMENT) {
if (this.volumeLimit) {
const current_volume_device = await this.denonClient.getVolume();
if (this.volumeLimit - current_volume_device <= 0) {
// do nothing - volume limit reached
}
else if (this.volumeLimit - current_volume_device < this.volumeStepSize) {
await this.denonClient.setVolumeUp(this.volumeLimit - current_volume_device);
}
else {
await this.denonClient.setVolumeUp(this.volumeStepSize);
}
}
else {
await this.denonClient.setVolumeUp(this.volumeStepSize);
}
}
else if (direction === this.platform.Characteristic.VolumeSelector.DECREMENT) {
await this.denonClient.setVolumeDown(this.volumeStepSize);
}
}
catch (error) {
this.log.error(`An error occured while decrementing volume for ${this.name}.`, error);
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
}
finally {
this.volumeChangeOngoing = false;
}
}
/*
* Handles characteristic updates from the API
*/
callbackActive(active) {
this.tvService.updateCharacteristic(this.platform.Characteristic.Active, active);
}
callbackMute(mute) {
this.speakerService.updateCharacteristic(this.platform.Characteristic.Mute, mute);
}
callbackVolume(volume) {
this.speakerService.updateCharacteristic(this.platform.Characteristic.Volume, this.adjustBackFromVolumeLimit(volume));
}
callbackInput(inputID) {
const inputIdentifier = this.configuredInputsByInputId.get(inputID)?.identifier || this.unknownInput.identifier;
this.tvService.updateCharacteristic(this.platform.Characteristic.ActiveIdentifier, inputIdentifier);
}
/*
* Volume Limit Helpers
*/
adjustToVolumeLimit(volume) {
if (this.volumeLimit) {
return Math.round((volume * this.volumeLimit) / 100);
}
else {
return volume;
}
}
adjustBackFromVolumeLimit(volume) {
if (this.volumeLimit) {
if (this.lastSetVolume && this.adjustToVolumeLimit(this.lastSetVolume) === volume) {
return this.lastSetVolume;
}
else {
return Math.round((volume / this.volumeLimit) * 100);
}
}
else {
return volume;
}
}
}
class ConfiguredInput {
inputID;
displayName;
showInList;
identifier;
userDefined;
constructor(inputID, displayName, showInList, identifier, userDefined) {
this.inputID = inputID;
this.displayName = displayName;
this.showInList = showInList;
this.identifier = identifier;
this.userDefined = userDefined;
}
}
//# sourceMappingURL=platformAccessory.js.map