homebridge-pioneer-avr-2025
Version:
A Pioneer AVR plugin for homebridge. This plugin is designed for Pioneer models that use Telnet Commands (e.g., VSX-922). It may not be compatible with newer models (e.g., VSX-LX304).
971 lines • 59.4 kB
JavaScript
"use strict";
// src/pioneer-avr-accessory.ts
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const pioneerAvr_1 = __importDefault(require("./pioneer-avr/pioneerAvr"));
const fs_1 = __importDefault(require("fs")); // For file system operations
const path_1 = __importDefault(require("path")); // For handling file paths
const package_json_1 = __importDefault(require("../package.json"));
const exitHandler_1 = require("./exitHandler");
class PioneerAvrAccessory {
// private timeoutFunctionSetSwitchListeningMode: NodeJS.Timeout | null = null;
constructor(device, platform, accessory) {
this.device = device;
this.enabledServices = [];
this.inputCacheFile = '';
this.inputCache = {};
this.writeVisbilityTimeout = null;
this.telnetConnectedServiceSwitchDisconnectTimeout = null;
this.lastListeningSwitchPressTime = 0;
this.LOCK_INTERVAL_LISTENING_SWITCH = 2000;
/**
* Prepares the Switch service for check/control if telnet connected.
*/
this.timeoutFunctionSetSwitchTelnetConnected = null;
// Timestamp for the last input switch press
// Lock interval in milliseconds for input switch commands
this.lastInputSwitchPressTime = 0;
this.timeoutupdateInputSwitchStates = null;
this.LOCK_INTERVAL_INPUT_SWITCH = 3000;
this.device = device;
this.platform = platform;
this.accessory = accessory;
this.log = this.platform.log;
this.name = device.name || 'Pioneer AVR';
this.manufacturer = this.platform.config.manufacturer || 'Pioneer';
this.model =
this.platform.config.model || device.name || 'Unknown Model';
this.host = device.host || this.platform.config.host || '';
this.maxVolume = this.platform.config.maxVolume || 100;
this.prefsDir =
this.platform.config.prefsDir ||
this.platform.api.user.storagePath() + '/pioneerAvr/';
this.inputCacheFile = path_1.default.join(this.prefsDir, `inputCache_${this.host}.json`);
this.version = package_json_1.default.version;
this.name = this.name.replace(/[^a-zA-Z0-9 ']/g, '');
this.name = this.name
.replace(/^[^a-zA-Z0-9]+|[^a-zA-Z0-9]+$/g, '')
.trim();
this.log.info(`Creating accessory ${this.name} for: ${this.device.origName} at ${this.device.host}:${this.device.port}`);
try {
this.avr = new pioneerAvr_1.default(platform, this, () => __awaiter(this, void 0, void 0, function* () {
var _a, _b;
try {
while (!this.avr) {
yield new Promise((resolve) => setTimeout(resolve, 180));
}
this.enabledServices = [];
yield this.prepareInformationService();
yield this.prepareTvService();
yield this.prepareTvSpeakerService();
if ((_a = this.platform.config.toggleListeningMode) !== null && _a !== void 0 ? _a : true) {
yield this.prepareListeningService();
}
if (this.maxVolume !== 0) {
yield this.prepareVolumeService();
}
if ((_b = this.platform.config.telnetSwitch) !== null && _b !== void 0 ? _b : true) {
yield this.prepareTelnetConnectedService();
}
this.log.info(`> Finished initializing. Device ${this.name} ready!`);
}
catch (err) {
this.log.debug('Error during AVR setup callback:', err);
}
}));
this.avr.addInputSourceService =
this.addInputSourceService.bind(this);
}
catch (err) {
this.log.debug('Error initializing AVR:', err);
}
// addExitHandler(() => {
// if (this.writeVisbilityTimeout) {
// clearTimeout(this.writeVisbilityTimeout);
// }
// }, this);
}
handleInputSwitches() {
return __awaiter(this, void 0, void 0, function* () {
if (this.device.inputSwitches && Array.isArray(this.device.inputSwitches) && this.device.inputSwitches.length > 0) {
yield this.addInputSwitch(this.device.host, this.device.inputSwitches);
}
});
}
/**
* Prepares the accessory's information service.
*/
prepareInformationService() {
return __awaiter(this, void 0, void 0, function* () {
this.informationService =
this.accessory.getService(this.platform.service.AccessoryInformation) ||
this.accessory.addService(this.platform.service.AccessoryInformation);
this.informationService
.setCharacteristic(this.platform.characteristic.Name, this.device.name.replace(/(^[^a-zA-Z0-9]+)|([^a-zA-Z0-9]+$)|([^a-zA-Z0-9 '])/g, ''))
.setCharacteristic(this.platform.characteristic.Manufacturer, this.manufacturer)
.setCharacteristic(this.platform.characteristic.Model, this.model)
.setCharacteristic(this.platform.characteristic.SerialNumber, this.device.origName.replace(/(^[^a-zA-Z0-9]+)|([^a-zA-Z0-9]+$)|([^a-zA-Z0-9\-\. '])/g, ''))
.setCharacteristic(this.platform.characteristic.FirmwareRevision, this.version);
this.enabledServices.push(this.informationService);
});
}
/**
* Sets up the Television service to control the AVR's power and input selection.
*/
prepareTvService() {
return __awaiter(this, void 0, void 0, function* () {
this.tvService =
this.accessory.getService(this.platform.service.Television) ||
this.accessory.addService(this.platform.service.Television, this.name, 'tvService');
this.tvService
.setCharacteristic(this.platform.characteristic.ConfiguredName, this.name)
.setCharacteristic(this.platform.characteristic.SleepDiscoveryMode, this.platform.characteristic.SleepDiscoveryMode
.ALWAYS_DISCOVERABLE);
(0, exitHandler_1.addExitHandler)(() => {
this.tvService.updateCharacteristic(this.platform.characteristic.SleepDiscoveryMode, this.platform.characteristic.SleepDiscoveryMode
.NOT_DISCOVERABLE);
}, this);
this.tvService
.getCharacteristic(this.platform.characteristic.Active)
.onGet(this.getPowerOn.bind(this))
.onSet(this.setPowerOn.bind(this));
this.tvService
.getCharacteristic(this.platform.characteristic.ActiveIdentifier)
.onGet(this.getActiveIdentifier.bind(this))
.onSet(this.setActiveIdentifier.bind(this));
this.tvService
.getCharacteristic(this.platform.characteristic.RemoteKey)
.onSet(this.remoteKeyPress.bind(this));
this.enabledServices.push(this.tvService);
this.avr.functionSetPowerState = (set) => {
try {
const boolToNum = set ? 1 : 0;
if (this.tvService.getCharacteristic(this.platform.characteristic.Active).value !== boolToNum) {
// console.log('functionSetPowerState SET', boolToNum)
this.tvService.updateCharacteristic(this.platform.characteristic.SleepDiscoveryMode, !boolToNum);
this.tvService.updateCharacteristic(this.platform.characteristic.Active, boolToNum);
}
}
catch (e) {
this.log.debug('Error functionSetPowerState:', e);
}
};
this.avr.functionSetPowerState(this.avr.state.on);
this.avr.functionSetActiveIdentifier = (set) => {
if (this.tvService.getCharacteristic(this.platform.characteristic.ActiveIdentifier).value !== set) {
this.tvService.updateCharacteristic(this.platform.characteristic.ActiveIdentifier, set);
}
};
});
}
/**
* Prepares the Television Speaker service for volume control.
*/
prepareTvSpeakerService() {
return __awaiter(this, void 0, void 0, function* () {
while (!this.tvService ||
!this.enabledServices.includes(this.tvService)) {
yield new Promise((resolve) => setTimeout(resolve, 180));
}
this.tvSpeakerService =
this.accessory.getService(this.platform.service.TelevisionSpeaker) ||
this.accessory.addService(this.platform.service.TelevisionSpeaker, this.name + ' Speaker', 'tvSpeakerService');
this.tvSpeakerService
.getCharacteristic(this.platform.characteristic.Active)
.onGet(this.getMutedInverted.bind(this))
.onSet(this.setMutedInverted.bind(this));
this.tvSpeakerService
// .setCharacteristic(this.platform.characteristic.Active, this.platform.characteristic.Active.ACTIVE)
.setCharacteristic(this.platform.characteristic.VolumeControlType, this.platform.characteristic.VolumeControlType.RELATIVE);
// .setCharacteristic(this.platform.characteristic.VolumeControlType, this.platform.characteristic.VolumeControlType.ABSOLUTE);
this.tvSpeakerService
.getCharacteristic(this.platform.characteristic.VolumeSelector)
.onSet(this.setVolumeSwitch.bind(this));
this.tvSpeakerService
.getCharacteristic(this.platform.characteristic.Mute)
.onGet(this.getMuted.bind(this))
.onSet(this.setMuted.bind(this));
this.tvSpeakerService
.getCharacteristic(this.platform.characteristic.Volume)
.onGet(this.getVolume.bind(this));
// this.tvSpeakerService.getCharacteristic(this.platform.characteristic.Volume)
// .onGet(this.getVolume.bind(this))
// .onSet(this.setVolume.bind(this));
this.tvService.addLinkedService(this.tvSpeakerService);
this.enabledServices.push(this.tvSpeakerService);
// this.log.debug('prepareTvSpeakerService enabled')
});
}
/**
* Prepares the Lightbulb service for volume control.
*/
prepareVolumeService() {
return __awaiter(this, void 0, void 0, function* () {
while (!this.tvService ||
!this.enabledServices.includes(this.tvService) ||
!this.tvSpeakerService ||
!this.enabledServices.includes(this.tvSpeakerService)) {
yield new Promise((resolve) => setTimeout(resolve, 180));
}
this.volumeServiceLightbulb =
this.accessory.getService(this.platform.service.Lightbulb) ||
this.accessory.addService(this.platform.service.Lightbulb, this.name + ' Volume', 'volumeInput');
this.volumeServiceLightbulb.addOptionalCharacteristic(this.platform.characteristic.ConfiguredName);
this.volumeServiceLightbulb.setCharacteristic(this.platform.characteristic.Name, 'Volume');
this.volumeServiceLightbulb.setCharacteristic(this.platform.characteristic.ConfiguredName, 'Volume');
this.volumeServiceLightbulb
.getCharacteristic(this.platform.characteristic.On)
.onGet(this.getMutedInverted.bind(this))
.onSet(this.setMutedInverted.bind(this));
this.volumeServiceLightbulb
.getCharacteristic(this.platform.characteristic.Brightness)
.onGet(this.getVolume.bind(this))
.onSet(this.setVolume.bind(this));
this.tvService.addLinkedService(this.volumeServiceLightbulb);
this.enabledServices.push(this.volumeServiceLightbulb);
this.avr.functionSetLightbulbVolume = (set) => {
try {
const currentBrightness = this.volumeServiceLightbulb.getCharacteristic(this.platform.characteristic.Brightness).value;
const currentOnState = this.volumeServiceLightbulb.getCharacteristic(this.platform.characteristic.On).value;
// Update Brightness only if it is different from the new volume
if (currentBrightness !== set) {
this.volumeServiceLightbulb.updateCharacteristic(this.platform.characteristic.Brightness, this.avr.state.muted || !this.avr.state.on ? 0 : set);
}
// Update On state based on mute and power status
if (currentOnState !==
!(this.avr.state.muted || !this.avr.state.on)) {
this.volumeServiceLightbulb.updateCharacteristic(this.platform.characteristic.On, !(this.avr.state.muted || !this.avr.state.on));
}
}
catch (e) {
this.log.debug('Error updating Lightbulb volume:', e);
}
};
// Initial volume setup only if volume state is valid
if (typeof this.avr.state.volume === 'number') {
this.avr.functionSetLightbulbVolume(this.avr.state.volume);
}
this.avr.functionSetLightbulbMuted = () => {
try {
const currentOnState = this.volumeServiceLightbulb.getCharacteristic(this.platform.characteristic.On).value;
if (currentOnState !==
!(this.avr.state.muted || !this.avr.state.on)) {
this.volumeServiceLightbulb.updateCharacteristic(this.platform.characteristic.On, !(this.avr.state.muted || !this.avr.state.on));
}
}
catch (e) {
this.log.debug('Error updating Lightbulb mute state:', e);
}
};
this.avr.functionSetLightbulbMuted(this.avr.state.muted);
});
}
prepareTelnetConnectedService() {
return __awaiter(this, void 0, void 0, function* () {
while (!this.tvService ||
!this.enabledServices.includes(this.tvService)) {
yield new Promise((resolve) => setTimeout(resolve, 180));
}
try {
let switchName = `${this.name} telnet`;
switchName = switchName.replace(/(^[^a-zA-Z0-9]+)|([^a-zA-Z0-9]+$)|([^a-zA-Z0-9 '])/g, '')
.trim();
this.log.debug(`Creating switch accessory: ${switchName} for: ${this.name}`);
const uuid = this.platform.api.hap.uuid.generate(`${this.host}-telnetState`);
// Check if accessory already exists
let accessory = this.platform.accessories.find((existing) => existing.UUID === uuid);
if (!accessory) {
accessory = new this.platform.api.platformAccessory(switchName, uuid);
this.platform.accessories.push(accessory);
this.platform.api.registerPlatformAccessories(this.platform.pluginName, this.platform.platformName, [accessory]);
}
// Add or update Switch service
this.telnetConnectedServiceSwitch =
accessory.getServiceById(this.platform.service.Switch, this.name + 'telnetState') ||
accessory.addService(this.platform.service.Switch, switchName, this.name + 'telnetState');
const informationService = accessory.getService(this.platform.service.AccessoryInformation) ||
accessory.addService(this.platform.service.AccessoryInformation);
informationService
.setCharacteristic(this.platform.characteristic.Name, switchName.replace(/(^[^a-zA-Z0-9]+)|([^a-zA-Z0-9]+$)|([^a-zA-Z0-9 '])/g, ''))
.setCharacteristic(this.platform.characteristic.Manufacturer, this.manufacturer)
.setCharacteristic(this.platform.characteristic.Model, this.model)
.setCharacteristic(this.platform.characteristic.SerialNumber, `${this.name}-telnetState`.replace(/(^[^a-zA-Z0-9]+)|([^a-zA-Z0-9]+$)|([^a-zA-Z0-9\-\. '])/g, ''))
.setCharacteristic(this.platform.characteristic.FirmwareRevision, this.version);
this.telnetConnectedServiceSwitch
.getCharacteristic(this.platform.characteristic.On)
.onGet(() => __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c, _d, _e, _f, _g, _h;
return !!(((_d = (_c = (_b = (_a = this.avr) === null || _a === void 0 ? void 0 : _a.telnetAvr) === null || _b === void 0 ? void 0 : _b.connection) === null || _c === void 0 ? void 0 : _c.socket) === null || _d === void 0 ? void 0 : _d.connecting) || ((_h = (_g = (_f = (_e = this.avr) === null || _e === void 0 ? void 0 : _e.telnetAvr) === null || _f === void 0 ? void 0 : _f.connection) === null || _g === void 0 ? void 0 : _g.socket) === null || _h === void 0 ? void 0 : _h.readyState) === 'open');
}))
.onSet((value) => __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
// this.log.debug('telnetConnectedServiceSwitch pressed:', value);
if (this.timeoutFunctionSetSwitchTelnetConnected) {
clearTimeout(this.timeoutFunctionSetSwitchTelnetConnected);
}
const currentState = !!(((_d = (_c = (_b = (_a = this.avr) === null || _a === void 0 ? void 0 : _a.telnetAvr) === null || _b === void 0 ? void 0 : _b.connection) === null || _c === void 0 ? void 0 : _c.socket) === null || _d === void 0 ? void 0 : _d.connecting) || ((_h = (_g = (_f = (_e = this.avr) === null || _e === void 0 ? void 0 : _e.telnetAvr) === null || _f === void 0 ? void 0 : _f.connection) === null || _g === void 0 ? void 0 : _g.socket) === null || _h === void 0 ? void 0 : _h.readyState) === 'open');
if (!!value !== !!currentState) {
if (!!value) {
//connect?
if (this.telnetConnectedServiceSwitchDisconnectTimeout) {
clearTimeout(this.telnetConnectedServiceSwitchDisconnectTimeout);
}
if (((_k = (_j = this.avr) === null || _j === void 0 ? void 0 : _j.telnetAvr) === null || _k === void 0 ? void 0 : _k.connection) && 'forcedDisconnect' in this.avr.telnetAvr.connection) {
this.avr.telnetAvr.connection.forcedDisconnect = false;
}
if ((_l = this.avr) === null || _l === void 0 ? void 0 : _l.lastUserInteraction) {
this.avr.lastUserInteraction = Date.now();
}
if ((_p = (_o = (_m = this.avr) === null || _m === void 0 ? void 0 : _m.telnetAvr) === null || _o === void 0 ? void 0 : _o.connection) === null || _p === void 0 ? void 0 : _p.connect) {
this.avr.telnetAvr.connection.connect();
}
}
else {
//disconnect?
let timeoutToDisconnect = 4900;
if ((_r = (_q = this.avr) === null || _q === void 0 ? void 0 : _q.state) === null || _r === void 0 ? void 0 : _r.on) {
timeoutToDisconnect = ((5 * 60 * 1000) + 4900);
}
else if ((_s = this.avr) === null || _s === void 0 ? void 0 : _s.lastUserInteraction) {
if ((((5 * 60 * 1000) + 4900) - (Date.now() - this.avr.lastUserInteraction)) > 5000) {
timeoutToDisconnect = (((5 * 60 * 1000) + 4900) - (Date.now() - this.avr.lastUserInteraction));
}
else {
timeoutToDisconnect = 4900;
}
// this.log.debug('lastUserInteraction', Date.now(), this.avr.lastUserInteraction, (Date.now() - this.avr.lastUserInteraction) / 1000, ((Date.now() - this.avr.lastUserInteraction) < (62 * 1000)));
if ((Date.now() - this.avr.lastUserInteraction) < (62 * 1000)) {
timeoutToDisconnect = 62 * 1000;
}
}
if (timeoutToDisconnect < 4900) {
timeoutToDisconnect = 4900;
}
if (this.telnetConnectedServiceSwitchDisconnectTimeout) {
clearTimeout(this.telnetConnectedServiceSwitchDisconnectTimeout);
}
if (timeoutToDisconnect < (60 * 1000) && ((_u = (_t = this.avr) === null || _t === void 0 ? void 0 : _t.telnetAvr) === null || _u === void 0 ? void 0 : _u.connection) && 'forcedDisconnect' in this.avr.telnetAvr.connection) {
this.avr.telnetAvr.connection.forcedDisconnect = true;
}
this.telnetConnectedServiceSwitchDisconnectTimeout = setTimeout(() => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0;
if (!((_b = (_a = this.avr) === null || _a === void 0 ? void 0 : _a.state) === null || _b === void 0 ? void 0 : _b.on) && (((_f = (_e = (_d = (_c = this.avr) === null || _c === void 0 ? void 0 : _c.telnetAvr) === null || _d === void 0 ? void 0 : _d.connection) === null || _e === void 0 ? void 0 : _e.socket) === null || _f === void 0 ? void 0 : _f.readyState) === 'open' || ((_k = (_j = (_h = (_g = this.avr) === null || _g === void 0 ? void 0 : _g.telnetAvr) === null || _h === void 0 ? void 0 : _h.connection) === null || _j === void 0 ? void 0 : _j.socket) === null || _k === void 0 ? void 0 : _k.connecting)) && (!((_l = this.avr) === null || _l === void 0 ? void 0 : _l.lastUserInteraction) ||
((Date.now() - this.avr.lastUserInteraction) > (61 * 1000)))) {
let runDisconnect = false;
if (((_m = this.avr) === null || _m === void 0 ? void 0 : _m.telnetAvr) && 'connectionReady' in this.avr.telnetAvr) {
if ((_p = (_o = this.avr) === null || _o === void 0 ? void 0 : _o.telnetAvr) === null || _p === void 0 ? void 0 : _p.connectionReady) {
runDisconnect = true;
}
this.avr.telnetAvr.connectionReady = false;
}
else {
runDisconnect = true;
}
if (runDisconnect) {
if (((_r = (_q = this.avr) === null || _q === void 0 ? void 0 : _q.telnetAvr) === null || _r === void 0 ? void 0 : _r.connection) && 'forcedDisconnect' in this.avr.telnetAvr.connection) {
this.avr.telnetAvr.connection.forcedDisconnect = true;
}
if ((_v = (_u = (_t = (_s = this.avr) === null || _s === void 0 ? void 0 : _s.telnetAvr) === null || _t === void 0 ? void 0 : _t.connection) === null || _u === void 0 ? void 0 : _u.messageQueue) === null || _v === void 0 ? void 0 : _v.clearQueue) {
this.avr.telnetAvr.connection.messageQueue.clearQueue();
}
if ((_y = (_x = (_w = this.avr) === null || _w === void 0 ? void 0 : _w.telnetAvr) === null || _x === void 0 ? void 0 : _x.connection) === null || _y === void 0 ? void 0 : _y.disconnect) {
setTimeout(() => {
this.avr.telnetAvr.connection.disconnect();
}, 100);
}
}
}
else if ((_0 = (_z = this.avr) === null || _z === void 0 ? void 0 : _z.state) === null || _0 === void 0 ? void 0 : _0.on) {
this.log.debug('the receiver is still on, no disconnect.');
}
if (this.timeoutFunctionSetSwitchTelnetConnected) {
clearTimeout(this.timeoutFunctionSetSwitchTelnetConnected);
}
this.avr.functionSetSwitchTelnetConnected();
}, timeoutToDisconnect);
this.log.debug('time to disconnect ms:', timeoutToDisconnect);
}
if (this.timeoutFunctionSetSwitchTelnetConnected) {
clearTimeout(this.timeoutFunctionSetSwitchTelnetConnected);
}
this.timeoutFunctionSetSwitchTelnetConnected = setTimeout(() => {
this.avr.functionSetSwitchTelnetConnected();
}, 5000);
}
}));
(0, exitHandler_1.addExitHandler)(() => {
this.telnetConnectedServiceSwitch.updateCharacteristic(this.platform.characteristic.On, false);
}, this);
this.enabledServices.push(this.telnetConnectedServiceSwitch);
this.avr.functionSetSwitchTelnetConnected = () => {
var _a, _b, _c, _d, _e, _f, _g, _h;
try {
const currentOnState = this.telnetConnectedServiceSwitch.getCharacteristic(this.platform.characteristic.On).value;
// Update On state based on whether a listening mode is active
if ((((_d = (_c = (_b = (_a = this.avr) === null || _a === void 0 ? void 0 : _a.telnetAvr) === null || _b === void 0 ? void 0 : _b.connection) === null || _c === void 0 ? void 0 : _c.socket) === null || _d === void 0 ? void 0 : _d.readyState) === 'open') !== currentOnState) {
this.telnetConnectedServiceSwitch.updateCharacteristic(this.platform.characteristic.On, !!(((_h = (_g = (_f = (_e = this.avr) === null || _e === void 0 ? void 0 : _e.telnetAvr) === null || _f === void 0 ? void 0 : _f.connection) === null || _g === void 0 ? void 0 : _g.socket) === null || _h === void 0 ? void 0 : _h.readyState) === 'open'));
}
}
catch (e) {
this.log.debug('Error updating Switch listening mode:', e);
}
};
// Initial listening mode setup
this.avr.functionSetSwitchTelnetConnected();
this.avr.telnetAvr.addOnDisconnectCallback(() => {
this.avr.functionSetSwitchTelnetConnected();
});
this.avr.telnetAvr.addOnConnectCallback(() => __awaiter(this, void 0, void 0, function* () {
this.avr.functionSetSwitchTelnetConnected();
}));
}
catch (error) {
this.log.error('telnetConnectedServiceSwitchError:', error);
}
});
}
/**
* Prepares the Switch service for listening mode control.
*/
prepareListeningService() {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b;
while (!this.tvService ||
!this.enabledServices.includes(this.tvService)) {
yield new Promise((resolve) => setTimeout(resolve, 180));
}
const isValidListeningMode = (value, defaultValue) => {
return /^[0-9]{4}$/.test(value || '') ? value : defaultValue;
};
const listeningModeOne = isValidListeningMode(this.device.listeningMode || this.platform.config.listeningMode, '0013'); // PRO LOGIC2 MOVIE
const listeningModeFallback = isValidListeningMode(this.device.listeningModeFallback || this.platform.config.listeningModeFallback, '0101'); // ACTION
// const listeningModeOther = isValidListeningMode(this.device.listeningModeOther || this.platform.config.listeningModeOther, '0112'); // EXTENDED STEREO
let switchName = `${this.name} Audio`;
switchName = switchName.replace(/(^[^a-zA-Z0-9]+)|([^a-zA-Z0-9]+$)|([^a-zA-Z0-9 '])/g, '')
.trim();
// Add or update Switch service
if ((_a = this.platform.config.toggleListeningModeLink) !== null && _a !== void 0 ? _a : true) {
this.listeningServiceSwitch =
this.accessory.getServiceById(this.platform.service.Switch, this.name + 'listeningMode') ||
this.accessory.addService(this.platform.service.Switch, switchName, this.name + 'listeningMode');
this.listeningServiceSwitch.addOptionalCharacteristic(this.platform.characteristic.ConfiguredName);
this.listeningServiceSwitch.setCharacteristic(this.platform.characteristic.Name, 'listeningMode');
this.listeningServiceSwitch.setCharacteristic(this.platform.characteristic.ConfiguredName, 'listeningMode');
}
else {
const uuid = this.platform.api.hap.uuid.generate(`${this.host}-listeningMode`);
// Check if accessory already exists
let accessory = this.platform.accessories.find((existing) => existing.UUID === uuid);
if (!accessory) {
accessory = new this.platform.api.platformAccessory(switchName, uuid);
this.platform.accessories.push(accessory);
this.platform.api.registerPlatformAccessories(this.platform.pluginName, this.platform.platformName, [accessory]);
}
this.listeningServiceSwitch =
accessory.getServiceById(this.platform.service.Switch, this.name + 'listeningMode') ||
accessory.addService(this.platform.service.Switch, switchName, this.name + 'listeningMode');
const informationService = accessory.getService(this.platform.service.AccessoryInformation) ||
accessory.addService(this.platform.service.AccessoryInformation);
informationService
.setCharacteristic(this.platform.characteristic.Name, switchName.replace(/(^[^a-zA-Z0-9]+)|([^a-zA-Z0-9]+$)|([^a-zA-Z0-9 '])/g, ''))
.setCharacteristic(this.platform.characteristic.Manufacturer, this.manufacturer)
.setCharacteristic(this.platform.characteristic.Model, this.model)
.setCharacteristic(this.platform.characteristic.SerialNumber, `${this.name}-listeningMode`.replace(/(^[^a-zA-Z0-9]+)|([^a-zA-Z0-9]+$)|([^a-zA-Z0-9\-\. '])/g, ''))
.setCharacteristic(this.platform.characteristic.FirmwareRevision, this.version);
}
this.listeningServiceSwitch
.getCharacteristic(this.platform.characteristic.On)
.onGet(() => __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c, _d, _e, _f;
let isOn = ((_b = (_a = this === null || this === void 0 ? void 0 : this.avr) === null || _a === void 0 ? void 0 : _a.state) === null || _b === void 0 ? void 0 : _b.on) && [listeningModeOne, listeningModeFallback].includes(this.avr.state.listeningMode || '');
if (((_d = (_c = this === null || this === void 0 ? void 0 : this.avr) === null || _c === void 0 ? void 0 : _c.state) === null || _d === void 0 ? void 0 : _d.on) && !((_f = (_e = this === null || this === void 0 ? void 0 : this.avr) === null || _e === void 0 ? void 0 : _e.state) === null || _f === void 0 ? void 0 : _f.listeningMode)) {
isOn = true;
}
return isOn;
}))
.onSet(() => __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c, _d;
const now = Date.now();
const timeSinceLastPress = now - this.lastListeningSwitchPressTime;
if (((_b = (_a = this === null || this === void 0 ? void 0 : this.avr) === null || _a === void 0 ? void 0 : _a.state) === null || _b === void 0 ? void 0 : _b.on) && !((_d = (_c = this === null || this === void 0 ? void 0 : this.avr) === null || _c === void 0 ? void 0 : _c.state) === null || _d === void 0 ? void 0 : _d.listeningMode)) {
return;
}
if (timeSinceLastPress < this.LOCK_INTERVAL_LISTENING_SWITCH) {
const remaining = this.LOCK_INTERVAL_LISTENING_SWITCH - timeSinceLastPress;
this.log.debug(`Listening switch pressed too soon, ignoring press. ${remaining} ms remaining.`);
// Reset the switch state to the actual current state
const currentState = this.avr.state.on &&
[listeningModeOne, listeningModeFallback].includes(this.avr.state.listeningMode || '');
this.listeningServiceSwitch
.getCharacteristic(this.platform.characteristic.On)
.updateValue(currentState);
return;
}
this.lastListeningSwitchPressTime = now;
this.avr.toggleListeningMode();
}));
(0, exitHandler_1.addExitHandler)(() => {
this.listeningServiceSwitch.updateCharacteristic(this.platform.characteristic.On, false);
}, this);
if ((_b = this.platform.config.toggleListeningModeLink) !== null && _b !== void 0 ? _b : true) {
this.tvService.addLinkedService(this.listeningServiceSwitch);
}
this.enabledServices.push(this.listeningServiceSwitch);
this.avr.functionSetSwitchListeningMode = () => __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c, _d, _e, _f, _g;
if (!((_b = (_a = this === null || this === void 0 ? void 0 : this.avr) === null || _a === void 0 ? void 0 : _a.state) === null || _b === void 0 ? void 0 : _b.on) && (!this.tvService ||
!this.enabledServices.includes(this.tvService) ||
!((_c = this === null || this === void 0 ? void 0 : this.avr) === null || _c === void 0 ? void 0 : _c.isReady) || !((_e = (_d = this === null || this === void 0 ? void 0 : this.avr) === null || _d === void 0 ? void 0 : _d.state) === null || _e === void 0 ? void 0 : _e.listeningMode))) {
this.listeningServiceSwitch.updateCharacteristic(this.platform.characteristic.On, false);
return;
}
if (this.avr.state.on && !this.avr.state.listeningMode) {
yield ((_g = (_f = this === null || this === void 0 ? void 0 : this.avr) === null || _f === void 0 ? void 0 : _f.__updateListeningMode) === null || _g === void 0 ? void 0 : _g.call(_f, () => { }));
}
try {
const currentOnState = this.listeningServiceSwitch.getCharacteristic(this.platform.characteristic.On).value;
const isValidListeningMode = (value, defaultValue) => {
return /^[0-9]{4}$/.test(value || '') ? value : defaultValue;
};
const listeningModeOne = isValidListeningMode(this.device.listeningMode || this.platform.config.listeningMode, '0013'); // PRO LOGIC2 MOVIE
const listeningModeFallback = isValidListeningMode(this.device.listeningModeFallback || this.platform.config.listeningModeFallback, '0101'); // ACTION
// const listeningModeOther = isValidListeningMode(this.device.listeningModeOther || this.platform.config.listeningModeOther, '0112'); // EXTENDED STEREO
const listeningModeActive = isValidListeningMode(this.avr.state.listeningMode || '', ''); // EXTENDED STEREO
const currentState = this.avr.state.on && [listeningModeOne, listeningModeFallback].includes(listeningModeActive);
// Update On state based on whether a listening mode is active
if (!!currentOnState !== !!currentState) {
this.listeningServiceSwitch.updateCharacteristic(this.platform.characteristic.On, !!currentState);
}
}
catch (e) {
this.log.debug('Error updating Switch listening mode:', e);
}
});
});
}
/**
* Prepares the input source service to allow selection of various AVR inputs.
*/
addInputSourceService(error, key) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b;
if (error) {
return;
}
while (!this.tvService ||
!this.enabledServices.includes(this.tvService)) {
yield new Promise((resolve) => setTimeout(resolve, 180));
}
if (!(key in this.avr.inputs)) {
this.log.error('addInputSourceService() input key not found.', key, this.avr.inputs);
return;
}
try {
const input = this.avr.inputs[key];
const tmpInput = this.accessory.getServiceById(this.platform.service.InputSource, key.toString()) ||
this.accessory.addService(this.platform.service.InputSource, input.name.replace(/(^[^a-zA-Z0-9]+)|([^a-zA-Z0-9]+$)|([^a-zA-Z0-9 '])/g, ''), key.toString());
tmpInput
.setCharacteristic(this.platform.characteristic.Identifier, key)
.setCharacteristic(this.platform.characteristic.ConfiguredName, input.name.replace(/(^[^a-zA-Z0-9]+)|([^a-zA-Z0-9]+$)|([^a-zA-Z0-9 '])/g, ''))
.setCharacteristic(this.platform.characteristic.IsConfigured, this.platform.characteristic.IsConfigured.CONFIGURED)
.setCharacteristic(this.platform.characteristic.InputSourceType, (_a = input.type) !== null && _a !== void 0 ? _a : 0)
.setCharacteristic(this.platform.characteristic.CurrentVisibilityState, this.avr.booleanToVisibilityState((_b = input.visible) !== null && _b !== void 0 ? _b : true));
tmpInput
.getCharacteristic(this.platform.characteristic.TargetVisibilityState)
.onSet((state) => {
// const state = this.avr.booleanToVisibilityState(true); // 0
// const isVisible = this.avr.visibilityStateToBoolean(1); // false
this.avr.inputs[key].visible = this.avr.visibilityStateToBoolean(parseInt(String(state), 10));
setTimeout(() => {
tmpInput.updateCharacteristic(this.platform.characteristic
.CurrentVisibilityState, state);
}, key * 233);
if (this.writeVisbilityTimeout) {
clearTimeout(this.writeVisbilityTimeout);
}
this.writeVisbilityTimeout = setTimeout(() => {
try {
if (fs_1.default.existsSync(this.inputCacheFile)) {
this.inputCache = JSON.parse(fs_1.default.readFileSync(this.inputCacheFile, 'utf-8'));
}
if (!this.inputCache) {
this.inputCache = {};
}
this.inputCache.inputs = this.avr.inputs;
fs_1.default.writeFile(this.inputCacheFile, JSON.stringify(this.inputCache), () => {
this.log.debug('saved visibility:');
});
}
catch (error) {
this.log.error('set visibility Error', error);
}
}, 15000);
});
tmpInput
.getCharacteristic(this.platform.characteristic.ConfiguredName)
.onSet((name) => {
this.avr.renameInput(input.id, String(name));
});
// console.log('add input to homebridge', key)
this.tvService.addLinkedService(tmpInput);
this.enabledServices.push(tmpInput);
}
catch (e) {
console.error('Error addInputSourceService:', e);
}
});
}
/**
* Returns the enabled HomeKit services.
*/
getServices() {
return this.enabledServices;
}
// Method to get the power status as a CharacteristicValue
getPowerOn() {
return __awaiter(this, void 0, void 0, function* () {
if (!this.avr.telnetAvr.connectionReady) {
return false;
}
return new Promise((resolve) => {
this.avr.powerStatus((error, status) => {
if (error) {
this.log.error('Error getting power status:', error);
resolve(false);
}
else {
resolve(status);
}
});
});
});
}
// Method to set the power status
setPowerOn(on) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c, _d;
// if (!this.avr.telnetAvr.connectionReady) {
// return;
// }
if (on) {
if (!((_b = (_a = this.avr) === null || _a === void 0 ? void 0 : _a.state) === null || _b === void 0 ? void 0 : _b.on)) {
this.avr.powerOn();
}
}
else {
if ((_d = (_c = this.avr) === null || _c === void 0 ? void 0 : _c.state) === null || _d === void 0 ? void 0 : _d.on) {
this.avr.powerOff();
}
}
});
}
getActiveIdentifier() {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve) => {
this.avr.inputStatus((_error, status) => {
resolve(status || 0);
});
});
});
}
setActiveIdentifier(newValue) {
return __awaiter(this, void 0, void 0, function* () {
// console.log('setActiveIdentifier called', newValue, typeof(newValue))
if (typeof newValue === 'number' &&
this.avr.telnetAvr.connectionReady) {
this.log.debug('set active identifier:', this.avr.inputs[newValue].name, this.avr.inputs[newValue].id);
this.avr.setInput(this.avr.inputs[newValue].id);
}
});
}
setVolumeSwitch(state) {
return __awaiter(this, void 0, void 0, function* () {
this.log.debug('setVolumeSwitch called:', state);
if (state !== 1) {
this.avr.volumeUp();
}
else {
this.avr.volumeDown();
}
});
}
// private async setVolumeSwitch(direction: CharacteristicValue): Promise<void> {
// // Check if direction is actually a number
// if (typeof direction === 'number') {
// // direction = 1 (volume down), direction = 0 (volume up)
// const adjustment = direction === 0 ? 1 : -3;
// const currentVolume = await this.getVolume();
//
// this.log.debug('setVolumeSwitch()currentVolume:', currentVolume)
//
// // Ensure currentVolume is a number
// const newVolume = typeof currentVolume === 'number'
// ? Math.min(Math.max(currentVolume + adjustment, 0), 100)
// : 0; // Set to 0 if currentVolume is not a valid value
//
// await this.setVolume(newVolume);
// this.log.debug('setVolumeSwitch called, adjusting volume from %s%% to: %s%%', currentVolume, newVolume);
// } else {
// this.log.debug('setVolumeSwitch called with invalid direction:', direction);
// }
// }
getVolume() {
return __awaiter(this, void 0, void 0, function* () {
// Extract the return value from volume as a number or default to 0 if undefined
return new Promise((resolve) => {
this.avr.volumeStatus((_error, volume) => {
resolve(typeof volume === 'number' ? volume : 0);
});
});
});
}
setVolume(volume) {
return __awaiter(this, void 0, void 0, function* () {
// Check if volume is a number before sending it to the device
if (typeof volume === 'number') {
this.avr.setVolume(volume);
}
else {
this.log.debug('setVolume called with invalid volume:', volume);
}
});
}
getMuted() {
return __awaiter(this, void 0, void 0, function* () {
if (!this.avr || this.avr.state.muted || !this.avr.state.on) {
return true;
}
return new Promise((resolve) => {
this.avr.muteStatus((_error, isMuted) => {
resolve(isMuted || false);
});
});
});
}
setMuted(mute) {
return __awaiter(this, void 0, void 0, function* () {
if (mute) {
this.avr.muteOn();
}
else {
this.avr.muteOff();
}
});
}
getMutedInverted() {
return __awaiter(this, void 0, void 0, function* () {
return !(!this.avr || this.avr.state.muted || !this.avr.state.on);
});
}
setMutedInverted(mute) {
return __awaiter(this, void 0, void 0, function* () {
if (!mute) {
this.avr.muteOn();
}
else {
this.avr.muteOff();
}
});
}
remoteKeyPress(remoteKey) {
return __awaiter(this, void 0, void 0, function* () {
switch (remoteKey) {
case this.platform.characteristic.RemoteKey.ARROW_UP:
this.avr.remoteKey('UP');
break;
case this.platform.characteristic.RemoteKey.ARROW_DOWN:
this.avr.remoteKey('DOWN');
break;
case this.platform.characteristic.RemoteKey.ARROW_LEFT:
this.avr.remoteKey('LEFT');
break;
case this.platform.characteristic.RemoteKey.ARROW_RIGHT:
this.avr.remoteKey('RIGHT');
break;
case this.platform.characteristic.RemoteKey.SELECT:
this.avr.remoteKey('ENTER');
break;
case this.platform.characteristic.RemoteKey.BACK:
this.avr.remoteKey('RETURN');
break;
case this.platform.characteristic.RemoteKey.PLAY_PAUSE:
this.avr.remoteKey('TOGGLE_PLAY_PAUSE');
break;
case this.platform.characteristic.RemoteKey.INFORMATION:
this.avr.remoteKey('HOME_MENU');
break;
default:
break;
}
});
}
updateInputSwitchStates(activeInputId) {
// Iterate through all accessories
this.platform.accessories.forEach((accessory) => {
var _a, _b, _c;
const service = accessory.getService(this.platform.service.Switch);
// not listeningMode
if (service && ((_a = accessory === null || accessory === void 0 ? void 0 : accessory.context) === null || _a === void 0 ? void 0 : _a.inputId)) {
// Check if the current accessory corresponds to the active input
// only input switches have inputId
const isActive = !!(((_c = (_b = this.avr) === null || _b === void 0 ? void 0 : _b.state) === null || _c === void 0 ? void 0 : _c.on) && (accessory.context.inputId === activeInputId));
// Update the switch state
service.getCharacteristic(this.platform.characteristic.On).updateValue(isActive);
}
});
// this.log.debug(`Switch states updated. Active input ID: ${activeInputId}`);
}
addInputSwitch(host, inputToSwitches) {
var _a;
const cachedInputs = ((_a = this.platform.cachedReceivers.get(host)) === null || _a === void 0 ? void 0 : _a.inputs) || [];
if (cachedInputs.length === 0) {
this.log.warn(`No cached inputs found for host: ${host}`);
return;
}
const validAccessories = []; // Track valid accessory UUIDs
inputToSwitches.slice(0, 5).sort((a, b) => parseInt(a, 10) - parseInt(b, 10)).forEach((inputId) => {
const input = cachedInputs.find((input) => input.id === inputId);
if (!input) {
this.log.warn(`Input ID ${inputId} not found for host: ${host}`);
return;
}
let switchName = `${input.name} ${this.name}`;
switchName = switchName.replace(/(^[^a-zA-Z0-9]+)|([^a-zA-Z0-9]+$)|([^a-zA-Z0-9 '])/g, '')
.trim();
this.log.debug(`Creating switch accessory: ${switchName} for host: ${host}`);
const uuid = this.platform.api.hap.uuid.generate(`${host}-${inputId}`);
validAccessories.push(uuid); // Mark this accessory as valid
// Check if accessory already exists
let accessory = this.platform.accessories.find((existing) => existing.UUID === uuid);
if (!accessory) {
accessory = new this.platform.api.platformAccessory(switchName, uuid);
accessory.context = {
host,
inputId: input.id,
inputName: input.name,
};
this.platform.accessories.push(accessory);
this.platform.api.registerPlatformAccessories(this.platform.pluginName, this.platform.platformName, [accessory]);
}
// Add or update Switch service
const switchService = accessory.getServiceById(this.platform.service.Switch, this.name + input.id) ||
accessory.addService(this.platform.service.Switch, switchName, this.name + input.id);
const inputIndex = this.avr.inputs.findIndex((findInput) => findInput.id === input.id);
// switchService.setCharacteristic(
// this.platform.characteristic.SerialNumber,
// `${host}-${input.id}-${input.name}`.replace(/(^[^a-zA-Z0-9]+)|([^a-zA-Z0-9]+$)|([^a-zA-Z0-9\-\. '])/g, ''),
// )
const informationService = accessory.getService(this.platform.service.AccessoryInformation) ||
accessory.addService(this.platform.service.AccessoryInformation);
informationService
.setCharacteristic(this.platform.characteristic.Name, switchName.replace(/(^[^a-zA-Z0-9]+)|([^a-zA-Z0-9]+$)|([^a-zA-Z0-9 '])/g, ''))
.setCharacteristic(this.platform.characteristic.Manufacturer, this.manufacturer)
.setCharacteristic(this.platform.characteristic.Model, this.model)
.setCharacteristic(this.platform.characteristic.SerialNumber, `${host}-${input.id}-${input.name}`.replace(/(^[^a-zA-Z0-9]+)|([^a-zA-Z0-9]+$)|([^a-zA-Z0-9\-\. '])/g, ''))
.setCharacteristic(this.platform.characteristic.FirmwareRevision, this.version);
// Configure 'On' characteristic
switchService
.getCharacteristic(this.platform.characteristic.On)
.onGet(() => __awaiter(this, void 0, void 0, function* () {
// Return true if the receiver is on and the input matches the switch index
const isOn = this.avr.state.on && inputIndex === this.avr.state.input;
return isOn;
}))
.onSet((value) => __awaiter(this, void 0, void 0, function* () {
var _a;
const now = Date.now();
const timeSinceLastPress = now - this.lastInputSwitchPressTime;
// If the last command was executed less than the lock interval ago, discard the press
if (timeSinceLastPress < this.LOCK_INTERVAL_INPUT_SWITCH) {
const remaining = this.LOCK_INTERVAL_INPUT_SWITCH - timeSinceLastPress;
this.log.debug(`Pressed too soon, ignoring press. ${remaining} ms remaining.`);
// Reset the switch state to the current state so the UI reflects the actual state
const currentState = this.avr.state.on && inputIndex === this.avr.state.input;
switchService.getCharacteristic(this.platform.characteristic.On).updateValue(currentState);
return;
}
// Update the timestamp after waiting the necessary time
this.lastInputSwitchPressTime = Date.now();
if (this.timeoutupdateInputSwitchStates) {
clearTimeout(this.timeoutupdateInputSwitchStates);
}
if ((_a = this.platform.config.toggleOffIfActive) !== null && _a !== void 0 ? _a : true) {
if (this.avr.state.on) {
if (this.avr.state.input === inputIndex) {
yield this.avr.powerOff();
return;
}
}
else {
yield this.avr.powerOn();
let c = 1000;
while (!this.avr.state.on && c-- > 0) {
yield new Promise((resolve) => setTimeout(resolve, 1555)); // Wait
}
yield new Promise((resolve) => setTimeout(resolve, 5000));
}
// Set the desired input
yield this.avr.setInput(input.id);
this.log.debug(`Input set to ${input.name} (${input.id})`);
this.timeoutupdateInputSwitchStates = setTimeout(() => {
// Update all switch states
this.updateInputSwitchStates(input.id);
}, 2000);
}
else if (value) {
// Turn on the receiver if it is off
if (!this.avr.state.on) {
yield this.avr.powerOn();
let c = 1000;
while (!this.avr.state.on && c-- > 0) {
yield new Promise((resolve) => setTimeout(resolve, 1555)); // Wait
}
yield new Promise((resolve) => setTimeout(resolve, 5000));
}
// Set the desired input
yield this.avr.setInput(input.id);
this.log.debug(`Input set to ${input.name} (${input.id})`);
this.timeoutupdateInputSwitchStates = setTimeout(() => {
// Update all switch states
this.updateInputSwitchStates(input.id);
}, 2000);
}
else {
this.log.debug(`Switch for ${switchName} turned off.`);
// Check if the receiver is still on and the input matches
if (this.avr.state.on && this.avr.state.input === inputIndex) {
yield this.avr.powerOff();
}
}
}));
(0, exitHandler_1.addExitHandler)(() => {
this.updateInputSwitchStates('-9999');
}, this);
this.log.info(`${this.name}> Switch accessory created for input: ${input.name} (${input.id}) on host: ${host}`);
});
// Cleanup invalid accessories
const validAccessoryUUIDs = new Set(validAccessories);
this.platform.accessories = this.platform.accessories.filter((accessory) => {
var _a;
const isValid = validAccessoryUUIDs.has(accessory.UUID);
if (!isValid && ((_a = accessory === null || accessory === void 0 ? void 0 : accessory.context) === null || _a === void 0 ? void 0 : _a.host) && host && accessory.context.host.toLowerCase() === host.toLowerCase()) {
this.log.info(`${this.name}> Removing accessory: ${accessory.displayName} (no longer valid)`);
this.platform.api.unregisterPlatformAccessories(this.platform.pluginName, this.platform.platformName, [accessory]);
}
return isValid || accessory.context.host !== host;
});
}
}
exports.default = PioneerAvrAccessory;
//# sourceMappingURL=pioneer-avr-accessory.js.map