Skip to content

Commit

Permalink
Refactor inputs services, prevent homebridge crash when AVR lost, & r…
Browse files Browse the repository at this point in the history
…econnect if connection reestablished

Fixes #9
Implement renamed inputs support as per #14
  • Loading branch information
ACDR committed Jan 11, 2020
1 parent 4f16220 commit cebaa4d
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 64 deletions.
172 changes: 109 additions & 63 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,22 @@ function YamahaAVRPlatform(log, config) {
}

/* Initialise Yamaha AVR Accessory */
function YamahaAVRAccessory(log, config, yamaha, sysConfig, inputs) {
function YamahaAVRAccessory(log, config, yamaha) {
this.log = log;

// Configuration
this.YAMAHA = yamaha;
this.config = config;
this.sysConfig = sysConfig;
this.sysConfig = null;
this.name = config.name || 'Yamaha AVR';
this.inputs = inputs;
this.inputs = [];
this.enabledServices = [];
this.inputServices = [];
this.playing = true;

this.setConfig();
this.setInputs();

// Check & Update Accessory Status every 5 seconds
this.checkStateInterval = setInterval(
this.checkAVRState.bind(this, this.updateAVRState.bind(this)),
Expand All @@ -41,50 +45,19 @@ module.exports = (homebridge) => {

YamahaAVRPlatform.prototype = {
accessories(callback) {
// Get Yamaha System Configuration
this.YAMAHA.getSystemConfig().then(
(sysConfig) => {
if (sysConfig && sysConfig.YAMAHA_AV) {
// Create the Yamaha AVR Accessory
if (this.config.inputs) {
callback([
new YamahaAVRAccessory(
this.log,
this.config,
this.YAMAHA,
sysConfig,
this.config.inputs,
),
]);
} else {
// If no inputs defined in config - set available inputs as returned from receiver
this.YAMAHA.getAvailableInputs().then((availableInputs) => {
callback([
new YamahaAVRAccessory(
this.log,
this.config,
this.YAMAHA,
sysConfig,
availableInputs,
),
]);
});
}
}
},
(ERROR) => {
this.log(`ERROR: Failed getSystemConfig from ${this.config.name} probably just not a Yamaha AVR.`, ERROR);
},
);
callback([
new YamahaAVRAccessory(
this.log,
this.config,
this.YAMAHA,
),
]);
},
};

YamahaAVRAccessory.prototype = {
/* Services */
getServices() {
this.log(`Initialised ${this.name}`);

// Services
this.informationService();
this.televisionService();
this.televisionSpeakerService();
Expand All @@ -100,8 +73,8 @@ YamahaAVRAccessory.prototype = {
.setCharacteristic(Characteristic.Name, this.name)
.setCharacteristic(Characteristic.Manufacturer, 'Yamaha')
// .setCharacteristic(Characteristic.FirmwareRevision, require('./package.json').version)
.setCharacteristic(Characteristic.Model, this.sysConfig.YAMAHA_AV.System[0].Config[0].Model_Name[0])
.setCharacteristic(Characteristic.SerialNumber, this.sysConfig.YAMAHA_AV.System[0].Config[0].System_ID[0]);
.setCharacteristic(Characteristic.Model, this.sysConfig ? this.sysConfig.YAMAHA_AV.System[0].Config[0].Model_Name[0] : 'Unknown')
.setCharacteristic(Characteristic.SerialNumber, this.sysConfig ? this.sysConfig.YAMAHA_AV.System[0].Config[0].System_ID[0] : 'Unknown');

this.enabledServices.push(this.informationService);
},
Expand Down Expand Up @@ -134,41 +107,42 @@ YamahaAVRAccessory.prototype = {
},

televisionSpeakerService() {
this.tvSpeakerService = new Service.TelevisionSpeaker(`${this.name} Volume`, 'tvSpeakerService');
this.tvSpeakerService
.setCharacteristic(Characteristic.Active, Characteristic.Active.ACTIVE)
.setCharacteristic(Characteristic.VolumeControlType, Characteristic.VolumeControlType.ABSOLUTE);

this.tvSpeakerService
.getCharacteristic(Characteristic.VolumeSelector)
.on('set', (direction, callback) => {
this.setVolume(direction, callback);
});
this.tvSpeakerService = new Service.TelevisionSpeaker(`${this.name} AVR`, 'tvSpeakerService');
this.tvSpeakerService
.setCharacteristic(Characteristic.Active, Characteristic.Active.ACTIVE)
.setCharacteristic(Characteristic.VolumeControlType, Characteristic.VolumeControlType.ABSOLUTE);

this.tvSpeakerService
.getCharacteristic(Characteristic.VolumeSelector)
.on('set', (direction, callback) => {
this.setVolume(direction, callback);
});

this.tvService.addLinkedService(this.tvSpeakerService);
this.enabledServices.push(this.tvSpeakerService);
this.tvService.addLinkedService(this.tvSpeakerService);
this.enabledServices.push(this.tvSpeakerService);
},

inputSourceServices() {
this.inputs.forEach((input, i) => {
const inputService = new Service.InputSource(input.id, `inputSource${i}`);
for (let i = 0; i < 50; i++) {
const inputService = new Service.InputSource(i, `inputSource_${i}`);

inputService
.setCharacteristic(Characteristic.Identifier, i)
.setCharacteristic(Characteristic.ConfiguredName, input.name)
.setCharacteristic(Characteristic.IsConfigured, Characteristic.IsConfigured.CONFIGURED)
.setCharacteristic(Characteristic.ConfiguredName, `Input ${i < 9 ? `0${i + 1}` : i + 1}`)
.setCharacteristic(Characteristic.IsConfigured, Characteristic.IsConfigured.NOT_CONFIGURED)
.setCharacteristic(Characteristic.InputSourceType, Characteristic.InputSourceType.APPLICATION)
.setCharacteristic(Characteristic.CurrentVisibilityState, Characteristic.CurrentVisibilityState.SHOWN);
.setCharacteristic(Characteristic.CurrentVisibilityState, Characteristic.CurrentVisibilityState.HIDDEN);

inputService
.getCharacteristic(Characteristic.ConfiguredName)
.on('set', (value, callback) => {
callback();
callback(null, value);
});

this.tvService.addLinkedService(inputService);
this.inputServices.push(inputService);
this.enabledServices.push(inputService);
});
}
},

/* State Handlers */
Expand All @@ -177,6 +151,9 @@ YamahaAVRAccessory.prototype = {
},

updateAVRState(error, status) {
this.setConfig();
this.setInputs();

if (this.tvService) {
this.tvService.getCharacteristic(Characteristic.Active).updateValue(status);

Expand All @@ -186,7 +163,7 @@ YamahaAVRAccessory.prototype = {
if (input.id === basicInfo.getCurrentInput()) {
// Get and update homekit accessory with the current set input
if (this.tvService.getCharacteristic(Characteristic.ActiveIdentifier).value !== index) {
this.log('Updating input', input.name, input.id);
this.log(`Updating input from ${input.name} to ${input.name}`);
return this.tvService.getCharacteristic(Characteristic.ActiveIdentifier).updateValue(index);
}
}
Expand All @@ -198,6 +175,75 @@ YamahaAVRAccessory.prototype = {
}
},

setConfig() {
this.YAMAHA.getSystemConfig().then(
sysConfig => {
if (sysConfig) {
this.sysConfig = sysConfig;
this.informationService.getCharacteristic(Characteristic.Model).updateValue(this.sysConfig.YAMAHA_AV.System[0].Config[0].Model_Name[0]);
this.informationService.getCharacteristic(Characteristic.SerialNumber).updateValue(this.sysConfig.YAMAHA_AV.System[0].Config[0].System_ID[0]);
}
},
error => {
this.log(`Failed to get system config from ${this.config.name}. Please verify the AVR is connected and accessible at ${this.config.ip}`);
}
);
},

setInputs() {
// If no inputs defined in config - set available inputs as returned from receiver
if (this.sysConfig) {
this.YAMAHA.getAvailableInputsWithNames().then(
availableInputs => {
const sanitizedInputs = [];

let i = 0;

for (const key in availableInputs[0]) {
// check if the property/key is defined in the object itself, not in parent
if (availableInputs[0].hasOwnProperty(key)) {
const id = String(key).replace('_', '');
const input = {
id: id,
name: availableInputs[0][key][0],
index: i
};
sanitizedInputs.push(input);
i++;
}
}

this.inputs = sanitizedInputs;

if (this.config.inputs && this.config.inputs.length > 0) {
const filteredInputs = [];

this.inputs.forEach((input, i) => {
sanitizedInputs.forEach(sanitizedInput => {
if (sanitizedInput.id === input.id) {
input.index = i;
filteredInputs.push(input);
}
});
});

this.inputs = filteredInputs;
}

this.inputs.forEach((input, i) => {
const inputService = this.inputServices[i];
inputService.getCharacteristic(Characteristic.ConfiguredName).updateValue(input.name);
inputService.getCharacteristic(Characteristic.IsConfigured).updateValue(Characteristic.IsConfigured.CONFIGURED);
inputService.getCharacteristic(Characteristic.CurrentVisibilityState).updateValue(Characteristic.CurrentVisibilityState.SHOWN);
});
},
error => {
this.log(`Failed to get available inputs from ${this.config.name}. Please verify the AVR is connected and accessible at ${this.config.ip}`);
}
);
}
},

getPowerState(callback) {
this.YAMAHA.isOn().then(
(RESULT) => {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"license": "UNLICENSED",
"version": "1.0.0",
"version": "1.0.1",
"name": "homebridge-yamaha-avr",
"description": "homebridge-plugin - Add Yamaha AVR as TV with input control",
"dependencies": {
Expand Down

0 comments on commit cebaa4d

Please sign in to comment.