diff --git a/src/accessories/deviceButton.ts b/src/accessories/deviceButton.ts index b456e48..ddf8574 100644 --- a/src/accessories/deviceButton.ts +++ b/src/accessories/deviceButton.ts @@ -69,7 +69,7 @@ export class DeviceButton { //Get device command if we don't have it if (!this._device) { - this._device = this._dataProvider.getDeviceFromName( + this._device = this._dataProvider.getDeviceByName( this._deviceInfo.DeviceName ); } @@ -82,12 +82,18 @@ export class DeviceButton { //change state if stateful if (this._deviceInfo.IsStateful && this._buttonState != newState) { this._buttonState = newState; - await this._device.sendCommand(this._deviceInfo.ButtonName); + await this._dataProvider.sendCommand( + this._deviceInfo.ButtonName, + this._device + ); return callback(); } else if (!this._deviceInfo.IsStateful) { //Send the number of configured key presses for (let i = 0; i < this._deviceInfo.NumberOfKeyPresses; i++) { - await this._device.sendCommand(this._deviceInfo.ButtonName); + await this._dataProvider.sendCommand( + this._deviceInfo.ButtonName, + this._device + ); } this._switchService diff --git a/src/accessories/sequence.ts b/src/accessories/sequence.ts index 9f09dd9..a92a246 100644 --- a/src/accessories/sequence.ts +++ b/src/accessories/sequence.ts @@ -59,7 +59,7 @@ export class Sequence { if (!deviceName) { continue; } - const device = this._dataProvider.getDeviceFromName(deviceName); + const device = this._dataProvider.getDeviceByName(deviceName); if (device) { this._devices[deviceName] = device; } else { @@ -87,7 +87,7 @@ export class Sequence { step.DeviceCommand && device.supportsCommand(step.DeviceCommand) ) { - await device.sendCommand(step.DeviceCommand); + await this._dataProvider.sendCommand(step.DeviceCommand, device); } else { this._platform.log.warn( `Attempted to execute command ${step.DeviceCommand} on device ${step.DeviceName} but the device or command was not found` diff --git a/src/dataProviders/harmonyDataProvider.ts b/src/dataProviders/harmonyDataProvider.ts index e8eaada..b88a169 100644 --- a/src/dataProviders/harmonyDataProvider.ts +++ b/src/dataProviders/harmonyDataProvider.ts @@ -1,65 +1,83 @@ import { Logging } from "homebridge"; import { inject, injectable } from "tsyringe"; +import { ICommand } from "../models"; import { IConfig } from "../models/config"; import { IDeviceConfig } from "../models/config/deviceConfig"; import { IHub } from "../models/config/hub"; import { HarmonyDevice } from "../models/harmonyDevice"; import { HarmonyHub } from "../models/harmonyHub"; +import { sleep } from "../util"; @injectable() export class HarmonyDataProvider { private _hubs: { [hubName: string]: HarmonyHub } = {}; - private _hubsByDevice: { [deviceName: string]: string } = {}; + private _deviceConfigByName: { [deviceName: string]: IDeviceConfig } = {}; constructor( @inject("IConfig") private _config: IConfig, @inject("log") private _log: Logging ) { _config.Devices.forEach((deviceConfig: IDeviceConfig) => { - this._hubsByDevice[deviceConfig.Name] = deviceConfig.Hub; + this._deviceConfigByName[deviceConfig.Name] = deviceConfig; }); - // this._deviceConfigs = props.deviceConfigs; this.connect(_config.Hubs); this.emitInfo(); } - public async turnOnDevices(devices: Array): Promise { + public async powerOnDevices(devices: Array): Promise { await Promise.all( devices.map(async (device: HarmonyDevice) => { if (device && device.name) { if (!device.on) { this._log.info(`Turning on device ${device.name}`); - await device.powerOn(); + await this.powerOnDevice(device); } } }) ); } - public async turnOffDevices(devices: Array) { + public async powerOffDevices(devices: Array) { await Promise.all( //Turn off devices devices.map(async (device: HarmonyDevice) => { if (device) { if (device.on) { this._log.info(`Turning off device ${device.name}`); - await device.powerOff(); + await this.powerOffDevice(device); } } }) ); } + public async sendCommand( + commandName: string, + harmonyDevice: HarmonyDevice + ): Promise { + let command!: ICommand; + + commandName = this.getOverrideCommand(commandName, harmonyDevice); + + if (harmonyDevice.supportsCommand(commandName)) { + command = harmonyDevice.getCommand(commandName); + } + const hub = this.getHubByDevice(harmonyDevice); + await hub.sendCommand(command); + } + /** * Get the IDevice by name. * @param deviceName The device to retrieve. */ - public getDeviceFromName(deviceName: string): HarmonyDevice { + public getDeviceByName(deviceName: string): HarmonyDevice { let device: HarmonyDevice | undefined; try { device = - this._hubs[this._hubsByDevice[deviceName]].getDeviceByName(deviceName); + this._hubs[this._deviceConfigByName[deviceName].Hub].getDeviceByName( + deviceName + ); } catch (err) { this._log.info(`Error retrieving device from hub: ${err}`); } @@ -101,4 +119,49 @@ export class HarmonyDataProvider { }); } } + + private getHubByDevice(device: HarmonyDevice) { + return this._hubs[this._deviceConfigByName[device.name].Hub]; + } + + private async powerOnDevice(harmonyDevice: HarmonyDevice): Promise { + let powerOnCommand: string = "Power On"; + let powerToggleCommand: string = "Power Toggle"; + if (harmonyDevice.supportsCommand(powerOnCommand)) { + await this.sendCommand(powerOnCommand, harmonyDevice); + harmonyDevice.on = true; + } else if (harmonyDevice.supportsCommand(powerToggleCommand)) { + await this.sendCommand(powerToggleCommand, harmonyDevice); + harmonyDevice.on = true; + } else { + await this.sendCommand(powerOnCommand, harmonyDevice); + } + } + + private async powerOffDevice(harmonyDevice: HarmonyDevice): Promise { + let powerOffCommand: string = "Power Off"; + let powerToggleCommand: string = "Power Toggle"; + if (harmonyDevice.supportsCommand(powerOffCommand)) { + await this.sendCommand(powerOffCommand, harmonyDevice); + harmonyDevice.on = false; + } else if (harmonyDevice.supportsCommand(powerToggleCommand)) { + await this.sendCommand(powerToggleCommand, harmonyDevice); + harmonyDevice.on = false; + } + } + + private getOverrideCommand( + commandName: string, + harmonyDevice: HarmonyDevice + ) { + const deviceConfig: IDeviceConfig = + this._deviceConfigByName[harmonyDevice.name]; + if (!deviceConfig.Overrides) { + return commandName; + } + const overrideCommand = deviceConfig.Overrides.find( + (e) => e.Command == commandName + ); + return overrideCommand ? overrideCommand.Override : commandName; + } } diff --git a/src/models/config/deviceConfig.ts b/src/models/config/deviceConfig.ts index 97b74a0..116b3eb 100644 --- a/src/models/config/deviceConfig.ts +++ b/src/models/config/deviceConfig.ts @@ -1,4 +1,5 @@ export interface IDeviceConfig { - Name: string; - Hub: string; -} \ No newline at end of file + Name: string; + Hub: string; + Overrides: Array<{ Command: string; Override: string }>; +} diff --git a/src/models/harmonyDevice.ts b/src/models/harmonyDevice.ts index 1b6c394..aefc87e 100644 --- a/src/models/harmonyDevice.ts +++ b/src/models/harmonyDevice.ts @@ -10,15 +10,12 @@ export interface IHarmonyDeviceProps { } export class HarmonyDevice { - private _harmony: any; - private _log: any; private _commands: { [name: string]: ICommand } = {}; private _on: boolean; constructor(props: IHarmonyDeviceProps) { this.id = props.id; this.name = props.name; - this._harmony = props.harmony; this._on = false; this._commands = props.commands; } @@ -30,6 +27,10 @@ export class HarmonyDevice { return this._on; } + public set on(value: boolean) { + this._on = value; + } + public get commands(): { [name: string]: ICommand } { return this._commands; } @@ -43,51 +44,4 @@ export class HarmonyDevice { public getCommand(commandName: string): ICommand { return this._commands[commandName]; } - - public async powerOn(): Promise { - let powerOnCommand: string = "Power On"; - let powerToggleCommand: string = "Power Toggle"; - if (this.supportsCommand(powerOnCommand)) { - await this.sendCommand(powerOnCommand); - this._on = true; - } else if (this.supportsCommand(powerToggleCommand)) { - await this.sendCommand(powerToggleCommand); - this._on = true; - } - } - - public async powerOff(): Promise { - let powerOffCommand: string = "Power Off"; - let powerToggleCommand: string = "Power Toggle"; - if (this.supportsCommand(powerOffCommand)) { - await this.sendCommand(powerOffCommand); - this._on = false; - } else if (this.supportsCommand(powerToggleCommand)) { - await this.sendCommand(powerToggleCommand); - this._on = false; - } - } - - public async sendCommand(commandName: string): Promise { - let command!: ICommand; - if (this.supportsCommand(commandName)) { - command = this.getCommand(commandName); - } - - try { - //Execute command - //HACK to fix Harmon Kardon receiver not turning off - if (command.command === "PowerOff") { - for (let i = 0; i < 2; i++) { - await this._harmony.sendCommand(JSON.stringify(command)); - } - } - await this._harmony.sendCommand(JSON.stringify(command)); - - //Sleep - await sleep(800); - } catch (err) { - this._log(`ERROR - error sending command to harmony: ${err}`); - } - } } diff --git a/src/models/harmonyHub.ts b/src/models/harmonyHub.ts index 49c8b77..0d2c9e4 100644 --- a/src/models/harmonyHub.ts +++ b/src/models/harmonyHub.ts @@ -2,6 +2,7 @@ import { HarmonyDevice } from "./harmonyDevice"; const Harmony = require("harmony-websocket"); import { ICommand } from "./device"; import { EventEmitter } from "events"; +import { sleep } from "../util"; export class HarmonyHub extends EventEmitter { private _devices: { [deviceName: string]: HarmonyDevice } = {}; @@ -72,4 +73,22 @@ export class HarmonyHub extends EventEmitter { private connect = async (): Promise => { await this._harmony.Connect(this._ip); }; + + public async sendCommand(command: ICommand): Promise { + try { + //Execute command + //HACK to fix Harmon Kardon receiver not turning off + if (command.command === "PowerOff") { + for (let i = 0; i < 2; i++) { + await this._harmony.sendCommand(JSON.stringify(command)); + } + } + await this._harmony.sendCommand(JSON.stringify(command)); + + //Sleep + await sleep(800); + } catch (err) { + this._log(`ERROR - error sending command to harmony: ${err}`); + } + } } diff --git a/src/services/activityService.ts b/src/services/activityService.ts index 3cff169..4dca0fb 100644 --- a/src/services/activityService.ts +++ b/src/services/activityService.ts @@ -34,7 +34,7 @@ export class ActivityService { controlUnitName ); - await this._harmonyDataProvider.turnOnDevices(devicesToTurnOn); + await this._harmonyDataProvider.powerOnDevices(devicesToTurnOn); await this.assignDeviceInput(activity); @@ -47,7 +47,7 @@ export class ActivityService { lastActivity, activity ); - await this._harmonyDataProvider.turnOffDevices(devicesToTurnOff); + await this._harmonyDataProvider.powerOffDevices(devicesToTurnOff); this._stateDataProvider.updateState(activity, controlUnitName); }; @@ -66,7 +66,7 @@ export class ActivityService { controlUnitName, lastActivity ); - await this._harmonyDataProvider.turnOffDevices(devicesToTurnOff); + await this._harmonyDataProvider.powerOffDevices(devicesToTurnOff); this._stateDataProvider.removeState(controlUnitName); }; @@ -157,7 +157,7 @@ export class ActivityService { private buildPotentialDeviceList(activity: IActivity): HarmonyDevice[] { return activity.DeviceSetupList.map( (value: IDeviceSetupItem): HarmonyDevice => { - return this._harmonyDataProvider.getDeviceFromName(value.DeviceName); + return this._harmonyDataProvider.getDeviceByName(value.DeviceName); } ); } @@ -165,12 +165,15 @@ export class ActivityService { private async assignDeviceInput(activity: IActivity): Promise { await Promise.all( activity.DeviceSetupList.map(async (value: IDeviceSetupItem) => { - let device: HarmonyDevice = this._harmonyDataProvider.getDeviceFromName( + let device: HarmonyDevice = this._harmonyDataProvider.getDeviceByName( value.DeviceName ); if (device && device.supportsCommand(`Input${value.Input}`)) { - await device.sendCommand(`Input${value.Input}`); + await this._harmonyDataProvider.sendCommand( + `Input${value.Input}`, + device + ); } }) ); @@ -190,7 +193,7 @@ export class ActivityService { let outputCommandName: string = `Out ${output.OutputLetter}`; let matrixDevice: HarmonyDevice = - this._harmonyDataProvider.getDeviceFromName( + this._harmonyDataProvider.getDeviceByName( this._config.Matrix.DeviceName ); @@ -199,10 +202,22 @@ export class ActivityService { matrixDevice.supportsCommand(inputCommandName) && matrixDevice.supportsCommand(outputCommandName) ) { - await matrixDevice.sendCommand(outputCommandName); - await matrixDevice.sendCommand(inputCommandName); - await matrixDevice.sendCommand(outputCommandName); - await matrixDevice.sendCommand(inputCommandName); + await this._harmonyDataProvider.sendCommand( + outputCommandName, + matrixDevice + ); + await this._harmonyDataProvider.sendCommand( + inputCommandName, + matrixDevice + ); + await this._harmonyDataProvider.sendCommand( + outputCommandName, + matrixDevice + ); + await this._harmonyDataProvider.sendCommand( + inputCommandName, + matrixDevice + ); } } } diff --git a/src/services/commandService.ts b/src/services/commandService.ts index e666274..be4e841 100644 --- a/src/services/commandService.ts +++ b/src/services/commandService.ts @@ -23,7 +23,7 @@ export class CommandService { if (currentActivity) { let commandName: string = ""; - let device: HarmonyDevice = this._harmonyDataProvider.getDeviceFromName( + let device: HarmonyDevice = this._harmonyDataProvider.getDeviceByName( currentActivity.ControlDevice ); switch (key) { @@ -65,7 +65,7 @@ export class CommandService { } } - await device.sendCommand(commandName); + await this._harmonyDataProvider.sendCommand(commandName, device); } }; } diff --git a/src/services/volumeService.ts b/src/services/volumeService.ts index 0c7f8cb..5a48961 100644 --- a/src/services/volumeService.ts +++ b/src/services/volumeService.ts @@ -19,10 +19,11 @@ export class VolumeService { let currentActivity = this._stateDataProvider.getState(controlUnitName); if (currentActivity) { let volumeDevice: HarmonyDevice = - this._harmonyDataProvider.getDeviceFromName( - currentActivity.VolumeDevice - ); - await volumeDevice.sendCommand(volumeUpCommand); + this._harmonyDataProvider.getDeviceByName(currentActivity.VolumeDevice); + await this._harmonyDataProvider.sendCommand( + volumeUpCommand, + volumeDevice + ); } }; @@ -34,10 +35,11 @@ export class VolumeService { let currentActivity = this._stateDataProvider.getState(controlUnitName); if (currentActivity) { let volumeDevice: HarmonyDevice = - this._harmonyDataProvider.getDeviceFromName( - currentActivity.VolumeDevice - ); - await volumeDevice.sendCommand(volumeDownCommand); + this._harmonyDataProvider.getDeviceByName(currentActivity.VolumeDevice); + await this._harmonyDataProvider.sendCommand( + volumeDownCommand, + volumeDevice + ); } }; }