UNPKG

homebridge-denon-heos-audio

Version:

Control your Denon speakers via AVR Control and/or HeosCLI

220 lines 8.06 kB
import { CommandMode, DenonClient, findMapByValue, findValueByMap, InvalidResponseException, Playing } from "./denonClient.js"; import * as DenonProtocol from "./denonProtocol.js"; export class DenonClientAvrControl extends DenonClient { controlMode = DenonProtocol.ControlMode.AVRCONTROL; static PROTOCOL = { POWER: { GET: { COMMAND: "PW?", PARAMS: "", EXP_RES: /^PW(\w+)$/, }, SET: { COMMAND: "PW", PARAMS: "[VALUE]", EXP_RES: /^PW(\w+)$/, }, VALUES: { ON: { VALUE: "ON", MAP: true, }, OFF: { VALUE: "STANDBY", MAP: false, }, }, }, MUTE: { GET: { COMMAND: "MU?", PARAMS: "", EXP_RES: /^MU(\w+)$/, }, SET: { COMMAND: "MU", PARAMS: "[VALUE]", EXP_RES: /^MU(\w+)$/, }, VALUES: { ON: { VALUE: "ON", MAP: true, }, OFF: { VALUE: "OFF", MAP: false, }, }, }, VOLUME: { GET: { COMMAND: "MV?", PARAMS: "", EXP_RES: /^MV(\d{2})5?$/, }, SET: { COMMAND: "MV", PARAMS: "[VALUE]", EXP_RES: /^MV(\d{2})5?$/, }, }, VOLUME_UP: { SET: { COMMAND: "MVUP", }, }, VOLUME_DOWN: { SET: { COMMAND: "MVDOWN", }, }, }; constructor(serialNumber, host, connect_timeout, response_timeout, debugLogCallback, powerUpdateCallback, muteUpdateCallback, volumeUpdateCallback) { super(serialNumber, { host: host, port: DenonProtocol.ControlMode.AVRCONTROL, connect_timeout: connect_timeout, response_timeout: response_timeout, command_prefix: undefined, command_separator: "\r\n", response_separator: "\r", all_responses_to_generic: false, }, debugLogCallback, powerUpdateCallback, muteUpdateCallback, volumeUpdateCallback); this.connect(); } async subscribeToChangeEvents() { // not necessary - change events are automatically sent in AVR control } responseRouter(response) { if (this.responseCallback) { let out = undefined; if (this.responseCallback.expectedResponse) { const match = response.match(this.responseCallback.expectedResponse); if (match) { out = match[1] ?? match[0]; } } else { out = response; } if (out) { this.debugLog("Received response:", response); this.responseCallback.callback(out); this.responseCallback = undefined; if (this.params.all_responses_to_generic) { this.genericResponseHandler(response); } } } else { this.genericResponseHandler(response); } } genericResponseHandler(response) { this.debugLog("Received data event:", response); // Power let match = response.match(DenonClientAvrControl.PROTOCOL.POWER.GET.EXP_RES); if (match) { const mappedValue = findMapByValue(DenonClientAvrControl.PROTOCOL.POWER.VALUES, match[1]); if (mappedValue === undefined) { throw new InvalidResponseException("Unexpected power state", Object.values(DenonClientAvrControl.PROTOCOL.POWER.VALUES).map((value) => value.VALUE), match[1]); } if (this.powerUpdateCallback) { this.powerUpdateCallback(mappedValue); } return; } // Mute match = response.match(DenonClientAvrControl.PROTOCOL.MUTE.GET.EXP_RES); if (match) { const mappedValue = findMapByValue(DenonClientAvrControl.PROTOCOL.MUTE.VALUES, match[1]); if (mappedValue === undefined) { throw new InvalidResponseException("Unexpected mute state", Object.values(DenonClientAvrControl.PROTOCOL.MUTE.VALUES).map((value) => value.VALUE), match[1]); } if (this.muteUpdateCallback) { this.muteUpdateCallback(mappedValue); } return; } // Volume match = response.match(DenonClientAvrControl.PROTOCOL.VOLUME.GET.EXP_RES); if (match) { if (this.volumeUpdateCallback) { this.volumeUpdateCallback(Number(match[1])); } return; } } async getPower() { const response = await this.sendCommand(DenonClientAvrControl.PROTOCOL.POWER, CommandMode.GET, {}); const mappedValue = findMapByValue(DenonClientAvrControl.PROTOCOL.POWER.VALUES, response); return mappedValue; } async setPower(power) { const response = await this.sendCommand(DenonClientAvrControl.PROTOCOL.POWER, CommandMode.SET, { value: findValueByMap(DenonClientAvrControl.PROTOCOL.POWER.VALUES, power), }); const mappedValue = findMapByValue(DenonClientAvrControl.PROTOCOL.POWER.VALUES, response); return mappedValue; } async getPlaying() { // not supported return Playing.UNSUPPORTED; } async setPlaying() { // not supported return Playing.UNSUPPORTED; } async setPlayNext() { // not supported } async setPlayPrevious() { // not supported } async getMute() { const response = await this.sendCommand(DenonClientAvrControl.PROTOCOL.MUTE, CommandMode.GET, {}); const mappedValue = findMapByValue(DenonClientAvrControl.PROTOCOL.MUTE.VALUES, response); return mappedValue; } async setMute(mute) { const response = await this.sendCommand(DenonClientAvrControl.PROTOCOL.MUTE, CommandMode.SET, { value: findValueByMap(DenonClientAvrControl.PROTOCOL.MUTE.VALUES, mute), }); const mappedValue = findMapByValue(DenonClientAvrControl.PROTOCOL.MUTE.VALUES, response); return mappedValue; } async getVolume() { const response = await this.sendCommand(DenonClientAvrControl.PROTOCOL.VOLUME, CommandMode.GET, {}); return Number(response); } async setVolume(volume) { if (volume < 0 || volume > 100) { throw new Error("Volume must be between 0 and 100"); } if (volume === 100) { volume = 99; } const response = await this.sendCommand(DenonClientAvrControl.PROTOCOL.VOLUME, CommandMode.SET, { value: Math.round(volume).toString().padStart(2, "0"), }); return Number(response); } async setVolumeUp(volumeIncrement) { if (volumeIncrement < 1 || volumeIncrement > 10) { throw new Error("Volume increment must be between 1 and 10"); } for (let i = 0; i < Math.round(volumeIncrement); i++) { await this.sendCommand(DenonClientAvrControl.PROTOCOL.VOLUME_UP, CommandMode.SET, {}); } } async setVolumeDown(volumeDecrement) { if (volumeDecrement < 1 || volumeDecrement > 10) { throw new Error("Volume decrement must be between 1 and 10"); } for (let i = 0; i < Math.round(volumeDecrement); i++) { await this.sendCommand(DenonClientAvrControl.PROTOCOL.VOLUME_DOWN, CommandMode.SET, {}); } } } //# sourceMappingURL=denonClientAvrControl.js.map