diff --git a/src/accessories/controlUnit.ts b/src/accessories/controlUnit.ts index 4c69b33..219ea31 100644 --- a/src/accessories/controlUnit.ts +++ b/src/accessories/controlUnit.ts @@ -1,5 +1,4 @@ import { PlatformAccessory, RemoteController, Service } from "homebridge"; -import HarmonyDataProvider from "../dataProviders/harmonyDataProvider"; import { IActivity } from "../models/config"; import { Platform } from "../platform"; import { ActivityService } from "../services/activityService"; @@ -151,7 +150,7 @@ export class ControlUnit { */ private onGetAccessoryActive = async () => { //@ts-ignore - return this._dataProvider.getIsActive(this._accessory.displayName) + return this._activityService.getIsActive(this._accessory.displayName) ? this._platform.Characteristic.Active.ACTIVE : this._platform.Characteristic.Active.INACTIVE; }; diff --git a/src/accessories/deviceButton.ts b/src/accessories/deviceButton.ts index 1c363d2..b456e48 100644 --- a/src/accessories/deviceButton.ts +++ b/src/accessories/deviceButton.ts @@ -1,5 +1,5 @@ import { PlatformAccessory, Service } from "homebridge"; -import HarmonyDataProvider from "../dataProviders/harmonyDataProvider"; +import { HarmonyDataProvider } from "../dataProviders/harmonyDataProvider"; import { IDeviceButton } from "../models/config"; import { HarmonyDevice } from "../models/harmonyDevice"; import { Platform } from "../platform"; diff --git a/src/accessories/sequence.ts b/src/accessories/sequence.ts index 7b05620..9f09dd9 100644 --- a/src/accessories/sequence.ts +++ b/src/accessories/sequence.ts @@ -5,7 +5,7 @@ import { PlatformAccessory, Service, } from "homebridge"; -import HarmonyDataProvider from "../dataProviders/harmonyDataProvider"; +import { HarmonyDataProvider } from "../dataProviders/harmonyDataProvider"; import { ISequence } from "../models/config/sequence"; import { HarmonyDevice } from "../models/harmonyDevice"; import { Platform } from "../platform"; diff --git a/src/dataProviders/harmonyDataProvider.ts b/src/dataProviders/harmonyDataProvider.ts index 63ff8ac..e8eaada 100644 --- a/src/dataProviders/harmonyDataProvider.ts +++ b/src/dataProviders/harmonyDataProvider.ts @@ -1,46 +1,20 @@ -import { IActivity } from "../models/config/activity"; -import { IDeviceSetupItem } from "../models/config/deviceSetupItem"; -import { IInput, IMatrix, IOutput } from "../models/config/matrix"; -import { RemoteKey } from "../accessories/controlUnit"; -import { EventEmitter } from "events"; -import { IHub } from "../models/config/hub"; +import { Logging } from "homebridge"; +import { inject, injectable } from "tsyringe"; +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 { IConfig } from "../models/config"; -import { inject, injectable } from "tsyringe"; -import { Logger, Logging } from "homebridge"; - -const Harmony = require("harmony-websocket"); - -interface IActivityState { - currentActivity: IActivity; -} - -interface IHarmonyDataProviderProps { - hubs: Array; - deviceConfigs: Array; - log: any; - matrix: IMatrix; -} @injectable() -class HarmonyDataProvider extends EventEmitter { - private _hubsByDevice: { [deviceName: string]: string } = {}; +export class HarmonyDataProvider { private _hubs: { [hubName: string]: HarmonyHub } = {}; - // private _devicesByHub: { [hubName: string]: { [deviceName: string]: HarmonyDevice } } = {}; - private _states: { - [controlUnitName: string]: IActivityState | undefined; - } = {}; - - private _matrix: IMatrix; + private _hubsByDevice: { [deviceName: string]: string } = {}; constructor( @inject("IConfig") private _config: IConfig, @inject("log") private _log: Logging ) { - super(); - this._matrix = _config.Matrix; _config.Devices.forEach((deviceConfig: IDeviceConfig) => { this._hubsByDevice[deviceConfig.Name] = deviceConfig.Hub; }); @@ -50,87 +24,9 @@ class HarmonyDataProvider extends EventEmitter { this.emitInfo(); } - // public get devicesByHub(): { [hubName: string]: { [deviceName: string]: HarmonyDevice } } { - // return this._devicesByHub; - // } - - public get hubs(): { [hubName: string]: HarmonyHub } { - return this._hubs; - } - - /** - * Power on all devices in an activity. - */ - public powerOn = async (controlUnitName: string, activity: IActivity) => { - //Only power on if not alread on - let currentActivity = this._states[controlUnitName] - ? this._states[controlUnitName]!.currentActivity - : undefined; - if (!currentActivity) { - await this.startActivity(controlUnitName, activity); - } - }; - - /** - * Power off all devices in an activity that aren't being used. - */ - public powerOff = async (controlUnitName: string) => { - if (!this._states[controlUnitName]) { - return; - } - //Build potential list of devices to turn off - let devicesToTurnOff: Array = this._states[ - controlUnitName - ]!.currentActivity.DeviceSetupList.map( - (value: IDeviceSetupItem): HarmonyDevice => { - return this.getDeviceFromName(value.DeviceName); - } - ); - - //Resolve device conflicts with other controlUnits - devicesToTurnOff = this.sanitizeDeviceList( - devicesToTurnOff, - controlUnitName - ); - - //Turn off devices - devicesToTurnOff.forEach(async (device: HarmonyDevice) => { - if (device) { - await device.powerOff(); - } - }); - - this._states[controlUnitName] = undefined; - }; - - /** - * Start an activity - */ - public startActivity = async ( - controlUnitName: string, - activity: IActivity - ) => { - this._log.info( - `Starting activity ${activity.DisplayName} for controlUnit: ${controlUnitName}` - ); - let lastActivity: IActivity | undefined = undefined; - if (this._states[controlUnitName]) { - lastActivity = this._states[controlUnitName]!.currentActivity; - } - - //Build potential list of devices to to turn on - let devicesToTurnOn: Array = activity.DeviceSetupList.map( - (value: IDeviceSetupItem): HarmonyDevice => { - return this.getDeviceFromName(value.DeviceName); - } - ); - - //Resolve device conflicts with other controlUnits - devicesToTurnOn = this.sanitizeDeviceList(devicesToTurnOn, controlUnitName); - - //Turn on devices + public async turnOnDevices(devices: Array): Promise { await Promise.all( - devicesToTurnOn.map(async (device: HarmonyDevice) => { + devices.map(async (device: HarmonyDevice) => { if (device && device.name) { if (!device.on) { this._log.info(`Turning on device ${device.name}`); @@ -139,188 +35,20 @@ class HarmonyDataProvider extends EventEmitter { } }) ); + } - //Assign correct input + public async turnOffDevices(devices: Array) { await Promise.all( - activity.DeviceSetupList.map(async (value: IDeviceSetupItem) => { - let device: HarmonyDevice = this.getDeviceFromName(value.DeviceName); - - if (device && device.supportsCommand(`Input${value.Input}`)) { - await device.sendCommand(`Input${value.Input}`); + //Turn off devices + devices.map(async (device: HarmonyDevice) => { + if (device) { + if (device.on) { + this._log.info(`Turning off device ${device.name}`); + await device.powerOff(); + } } }) ); - - if (activity.UseMatrix) { - //get input and output - let input: IInput = this._matrix.Inputs.filter( - (e) => e.InputDevice === activity.ControlDevice - )[0]; - let output: IOutput = this._matrix.Outputs.filter( - (e) => e.OutputDevice === activity.OutputDevice - )[0]; - - let inputCommandName: string = `In ${input.InputNumber}`; - let outputCommandName: string = `Out ${output.OutputLetter}`; - - let matrixDevice: HarmonyDevice = this.getDeviceFromName( - this._matrix.DeviceName - ); - - //Route hdmi - if ( - matrixDevice.supportsCommand(inputCommandName) && - matrixDevice.supportsCommand(outputCommandName) - ) { - await matrixDevice.sendCommand(outputCommandName); - await matrixDevice.sendCommand(inputCommandName); - await matrixDevice.sendCommand(outputCommandName); - await matrixDevice.sendCommand(inputCommandName); - } - } - - //Build potential list of devices to turn off - if (lastActivity) { - let devicesToTurnOff: Array = - lastActivity.DeviceSetupList.map( - (value: IDeviceSetupItem): HarmonyDevice => { - return this.getDeviceFromName(value.DeviceName); - } - ); - - //remove devices that will be used for next activity from list - //delete array[index] is stupid because it just nulls out the index. But now i have to deal with nulls - devicesToTurnOff.forEach((device: HarmonyDevice, index: number) => { - if ( - device && - device.name && - activity.DeviceSetupList.some((e) => { - return e && e.DeviceName === device.name; - }) - ) { - delete devicesToTurnOff[index]; - } - }); - - //Resolve device conflicts with other controlUnits - devicesToTurnOff = this.sanitizeDeviceList( - devicesToTurnOff, - controlUnitName - ); - - this._log.info( - `Sanatized devices to turn off: ${JSON.stringify( - devicesToTurnOff.map((e) => (e ? e.name : "")) - )}` - ); - - await Promise.all( - //Turn off devices - devicesToTurnOff.map(async (device: HarmonyDevice) => { - if (device) { - if (device.on) { - this._log.info(`Turning off device ${device.name}`); - await device.powerOff(); - } - } - }) - ); - } - - //Assign current activity - this._states[controlUnitName] = { currentActivity: activity }; - }; - - /** - * Turn the volume up for the current running activity. - */ - public volumeUp = async (controlUnitName: string) => { - let volumeUpCommand: string = "Volume Up"; - if (this._states[controlUnitName]) { - let volumeDevice: HarmonyDevice = this.getDeviceFromName( - this._states[controlUnitName]!.currentActivity.VolumeDevice - ); - await volumeDevice.sendCommand(volumeUpCommand); - } - }; - - /** - * Volume down for current running activity. - */ - public volumeDown = async (controlUnitName: string) => { - let volumeDownCommand: string = "Volume Down"; - if (this._states[controlUnitName]) { - let volumeDevice: HarmonyDevice = this.getDeviceFromName( - this._states[controlUnitName]!.currentActivity.VolumeDevice - ); - await volumeDevice.sendCommand(volumeDownCommand); - } - }; - - /** - * Send key press for current activity. - * - * @param controlUnitName The name of the control unit to act on. - * @param key The key to send. - */ - public sendKeyPress = async (controlUnitName: string, key: any) => { - if (this._states[controlUnitName]) { - let commandName: string = ""; - - let device: HarmonyDevice = this.getDeviceFromName( - this._states[controlUnitName]!.currentActivity.ControlDevice - ); - switch (key) { - case RemoteKey.ARROW_UP: { - commandName = "Direction Up"; - break; - } - case RemoteKey.ARROW_DOWN: { - commandName = "Direction Down"; - break; - } - case RemoteKey.ARROW_LEFT: { - commandName = "Direction Left"; - break; - } - case RemoteKey.ARROW_RIGHT: { - commandName = "Direction Right"; - break; - } - case RemoteKey.SELECT: { - commandName = "Select"; - break; - } - case RemoteKey.PLAY_PAUSE: { - commandName = "Pause"; - break; - } - case RemoteKey.INFORMATION: { - commandName = "Menu"; - break; - } - case RemoteKey.BACK: { - commandName = "Back"; - break; - } - case RemoteKey.EXIT: { - commandName = "Back"; - break; - } - } - - await device.sendCommand(commandName); - } - }; - - /** - * Return if a control unit is active - * @param controlUnitName - */ - public getIsActive(controlUnitName: string): IActivity | undefined { - return this._states[controlUnitName] - ? this._states[controlUnitName]!.currentActivity - : undefined; } /** @@ -339,20 +67,6 @@ class HarmonyDataProvider extends EventEmitter { return device!; } - // /** - // * Gets device button commands - // * @param deviceCommandName The device command name - // * @param deviceName The device name - // */ - // public getCommand(deviceCommandName: string, deviceName: string): ICommand | undefined { - // const device: HarmonyDevice = this.getDeviceFromName(deviceName); - // if (device && device.supportsCommand(deviceCommandName)) { - // return device.getCommand(deviceCommandName); - // } else { - // return undefined; - // } - // } - private connect = async (hubs: Array) => { let readyCount = 0; await Promise.all( @@ -362,7 +76,7 @@ class HarmonyDataProvider extends EventEmitter { newHarmonyHub.on("Ready", () => { readyCount++; if (readyCount === Object.keys(this._hubs).length) { - this.emit("Ready"); + // this.emit("Ready"); } }); await newHarmonyHub.initialize(); @@ -370,45 +84,11 @@ class HarmonyDataProvider extends EventEmitter { ); }; - /** - * Helper function to make sure no control unit depends on device list. - * @param devicesToTurnOn The list of devices to modify. - * @param controlUnitName The name of the control unit in question. - */ - private sanitizeDeviceList( - devicesToTurnOn: Array, - controlUnitName: string - ): Array { - for (let controlUnitKey in this._states) { - //Skip self - if (controlUnitKey === controlUnitName) { - continue; - } - let currentOtherState: IActivityState = this._states[controlUnitKey]!; - - if (currentOtherState) { - currentOtherState.currentActivity.DeviceSetupList.forEach( - (value: IDeviceSetupItem) => { - //there are devices to remove - if (devicesToTurnOn.some((e) => e && e.name === value.DeviceName)) { - let deviceToRemove: HarmonyDevice = devicesToTurnOn.filter( - (i) => i.name === value.DeviceName - )[0]; - delete devicesToTurnOn[devicesToTurnOn.indexOf(deviceToRemove)]; - } - } - ); - } - } - - return devicesToTurnOn; - } - private emitInfo(): void { //Emit devices if requested this._log.info("All hubs connected"); if (this._config.EmitDevicesOnStartup) { - const hubs = this.hubs; + const hubs = this._hubs; Object.values(hubs).forEach((hub: HarmonyHub) => { const deviceDictionary = hub.devices; this._log.info(`${hub.hubName}`); @@ -422,5 +102,3 @@ class HarmonyDataProvider extends EventEmitter { } } } - -export default HarmonyDataProvider; diff --git a/src/dataProviders/harmonyDataProvider2.ts b/src/dataProviders/harmonyDataProvider2.ts deleted file mode 100644 index c6705cf..0000000 --- a/src/dataProviders/harmonyDataProvider2.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { Logging } from "homebridge"; -import { inject } from "tsyringe"; -import { IConfig, IMatrix } 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"; - -export class HarmonyDataProvider2 { - private _matrix: IMatrix; - private _hubs: { [hubName: string]: HarmonyHub } = {}; - private _hubsByDevice: { [deviceName: string]: string } = {}; - - constructor( - @inject("IConfig") private _config: IConfig, - @inject("log") private _log: Logging - ) { - this._matrix = _config.Matrix; - _config.Devices.forEach((deviceConfig: IDeviceConfig) => { - this._hubsByDevice[deviceConfig.Name] = deviceConfig.Hub; - }); - // this._deviceConfigs = props.deviceConfigs; - - this.connect(_config.Hubs); - this.emitInfo(); - } - - public async turnOnDevices(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(); - } - } - }) - ); - } - - public async turnOffDevices(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(); - } - } - }) - ); - } - - /** - * Get the IDevice by name. - * @param deviceName The device to retrieve. - */ - public getDeviceFromName(deviceName: string): HarmonyDevice { - let device: HarmonyDevice | undefined; - try { - device = - this._hubs[this._hubsByDevice[deviceName]].getDeviceByName(deviceName); - } catch (err) { - this._log.info(`Error retrieving device from hub: ${err}`); - } - - return device!; - } - - private connect = async (hubs: Array) => { - let readyCount = 0; - await Promise.all( - hubs.map(async (hub: IHub): Promise => { - const newHarmonyHub = new HarmonyHub(hub.Name, hub.Ip, this._log.info); - this._hubs[hub.Name] = newHarmonyHub; - newHarmonyHub.on("Ready", () => { - readyCount++; - if (readyCount === Object.keys(this._hubs).length) { - // this.emit("Ready"); - } - }); - await newHarmonyHub.initialize(); - }) - ); - }; - - private emitInfo(): void { - //Emit devices if requested - this._log.info("All hubs connected"); - if (this._config.EmitDevicesOnStartup) { - const hubs = this._hubs; - Object.values(hubs).forEach((hub: HarmonyHub) => { - const deviceDictionary = hub.devices; - this._log.info(`${hub.hubName}`); - Object.values(deviceDictionary).forEach((device: HarmonyDevice) => { - this._log.info(` ${device.name} : ${device.id}`); - Object.keys(device.commands).forEach((command: string) => { - this._log.info(` ${command}`); - }); - }); - }); - } - } -} diff --git a/src/platform.ts b/src/platform.ts index 52e3942..7bed9bf 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -10,12 +10,11 @@ import { } from "homebridge"; import { ControlUnit, DeviceButton } from "./accessories"; import { Sequence } from "./accessories/sequence"; -import HarmonyDataProvider from "./dataProviders/harmonyDataProvider"; import { IConfig, IControlUnit, IDeviceButton } from "./models/config"; import { ISequence } from "./models/config/sequence"; import { PLATFORM_NAME, PLUGIN_NAME } from "./settings"; import { container } from "tsyringe"; -import { HarmonyDataProvider2 } from "./dataProviders/harmonyDataProvider2"; +import { HarmonyDataProvider } from "./dataProviders/harmonyDataProvider"; import { StateDataProvider } from "./dataProviders/stateDataProvider"; import { CommandService } from "./services/commandService"; import { ActivityService } from "./services/activityService"; @@ -221,9 +220,9 @@ export class Platform implements DynamicPlatformPlugin { container.register(Platform, { useValue: this }); container.register("IConfig", { useValue: this.config }); container.register("log", { useValue: this.log }); + container.registerSingleton(HarmonyDataProvider); + container.registerSingleton(ActivityService); container.registerSingleton(StateDataProvider); container.registerSingleton(CommandService); - container.registerSingleton(HarmonyDataProvider); - container.registerSingleton(HarmonyDataProvider2); } } diff --git a/src/services/activityService.ts b/src/services/activityService.ts index adf6910..3cff169 100644 --- a/src/services/activityService.ts +++ b/src/services/activityService.ts @@ -1,6 +1,6 @@ import { Logging } from "homebridge"; import { inject, injectable } from "tsyringe"; -import { HarmonyDataProvider2 } from "../dataProviders/harmonyDataProvider2"; +import { HarmonyDataProvider } from "../dataProviders/harmonyDataProvider"; import { StateDataProvider } from "../dataProviders/stateDataProvider"; import { IActivityState } from "../models/activityState"; import { @@ -15,8 +15,8 @@ import { HarmonyDevice } from "../models/harmonyDevice"; @injectable() export class ActivityService { constructor( - @inject(HarmonyDataProvider2) - private _harmonyDataProvider: HarmonyDataProvider2, + @inject(HarmonyDataProvider) + private _harmonyDataProvider: HarmonyDataProvider, @inject(StateDataProvider) private _stateDataProvider: StateDataProvider, @inject("IConfig") private _config: IConfig, @inject("log") private _log: Logging diff --git a/src/services/commandService.ts b/src/services/commandService.ts index d1fc2fd..e666274 100644 --- a/src/services/commandService.ts +++ b/src/services/commandService.ts @@ -1,15 +1,15 @@ -import { inject } from "tsyringe"; +import { inject, injectable } from "tsyringe"; import { RemoteKey } from "../accessories/controlUnit"; -import { HarmonyDataProvider2 } from "../dataProviders/harmonyDataProvider2"; +import { HarmonyDataProvider } from "../dataProviders/harmonyDataProvider"; import { StateDataProvider } from "../dataProviders/stateDataProvider"; -import { IConfig } from "../models/config"; import { HarmonyDevice } from "../models/harmonyDevice"; +@injectable() export class CommandService { constructor( @inject(StateDataProvider) private _stateDataProvider: StateDataProvider, - @inject(HarmonyDataProvider2) - private _harmonyDataProvider: HarmonyDataProvider2 + @inject(HarmonyDataProvider) + private _harmonyDataProvider: HarmonyDataProvider ) {} /** diff --git a/src/services/volumeService.ts b/src/services/volumeService.ts index 917d404..0c7f8cb 100644 --- a/src/services/volumeService.ts +++ b/src/services/volumeService.ts @@ -1,15 +1,14 @@ -import { Logging } from "homebridge"; -import { inject } from "tsyringe"; -import { HarmonyDataProvider2 } from "../dataProviders/harmonyDataProvider2"; +import { inject, injectable } from "tsyringe"; +import { HarmonyDataProvider } from "../dataProviders/harmonyDataProvider"; import { StateDataProvider } from "../dataProviders/stateDataProvider"; -import { IConfig } from "../models/config"; import { HarmonyDevice } from "../models/harmonyDevice"; +@injectable() export class VolumeService { constructor( @inject(StateDataProvider) private _stateDataProvider: StateDataProvider, - @inject(HarmonyDataProvider2) - private _harmonyDataProvider: HarmonyDataProvider2 + @inject(HarmonyDataProvider) + private _harmonyDataProvider: HarmonyDataProvider ) {} /**