From 5ddd1e1af00269c8627ec17127756d21f4155b82 Mon Sep 17 00:00:00 2001 From: watsonb8 Date: Thu, 23 Jan 2020 08:44:11 -0500 Subject: [PATCH 1/7] Progress commit --- package.json | 2 +- src/Accessories/DeviceButton.ts | 15 ++- src/DataProviders/HarmonyDataProvider.ts | 163 +++++++++++------------ src/Models/Config/IConfig.ts | 12 +- src/Models/Config/IDeviceButton.ts | 1 + src/Models/Config/IDeviceConfig.ts | 4 + src/Models/Config/IHub.ts | 5 + src/Models/HarmonyDevice.ts | 79 +++++++++++ src/Models/IDevice.ts | 9 +- 9 files changed, 195 insertions(+), 95 deletions(-) create mode 100644 src/Models/Config/IDeviceConfig.ts create mode 100644 src/Models/Config/IHub.ts create mode 100644 src/Models/HarmonyDevice.ts diff --git a/package.json b/package.json index ddc714a..0064c3c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "homebridge-harmony-control", - "version": "1.0.5", + "version": "1.0.6", "description": "Homebridge platform to control smart home equipment by room.", "main": "bin/index.js", "scripts": { diff --git a/src/Accessories/DeviceButton.ts b/src/Accessories/DeviceButton.ts index 4b66967..cbed4c3 100644 --- a/src/Accessories/DeviceButton.ts +++ b/src/Accessories/DeviceButton.ts @@ -48,6 +48,15 @@ export class DeviceButton implements IAccessory { this._buttonState = false; + if (this._buttonInfo.NumberOfKeyPresses && this._buttonInfo.IsStateful) { + throw new Error("A button cannot be stateful and be pressed more than once"); + } + + //Assign default number of key presses + if (!this._buttonInfo.NumberOfKeyPresses) { + this._buttonInfo.NumberOfKeyPresses = 1; + } + this.platformAccessory = new this._homebridge.platformAccessory(this.name, this.generateUUID(), this._homebridge.hap.Accessory.Categories.SWITCH); //@ts-ignore @@ -106,11 +115,13 @@ export class DeviceButton implements IAccessory { this._buttonState = newState await this._dataProvider.sendCommand(this._deviceCommand); } else if (!this._buttonInfo.IsStateful) { - await this._dataProvider.sendCommand(this._deviceCommand); + //Send the number of configured key presses + for (let i = 0; i < this._buttonInfo.NumberOfKeyPresses; i++) { + await this._dataProvider.sendCommand(this._deviceCommand); + } this._switchService.getCharacteristic(Characteristic.On).updateValue(false); return callback(new Error("Normal Response")); } - } return callback(); } diff --git a/src/DataProviders/HarmonyDataProvider.ts b/src/DataProviders/HarmonyDataProvider.ts index f273ecd..7386be3 100644 --- a/src/DataProviders/HarmonyDataProvider.ts +++ b/src/DataProviders/HarmonyDataProvider.ts @@ -5,6 +5,8 @@ import { RemoteKey } from '../Accessories/ControlUnit'; import { sleep } from '../Util/Sleep'; import { EventEmitter } from "events"; import { IDevice, ICommand } from '../Models/IDevice'; +import { IHub } from "../Models/Config/IHub"; +import { IDeviceConfig } from "../Models/Config/IDeviceConfig"; let Characteristic: HAPNodeJS.Characteristic; @@ -15,16 +17,16 @@ interface IActivityState { } interface IHarmonyDataProviderProps { - hubAddress: string, + hubs: Array; + deviceConfigs: Array; log: any, matrix: IMatrix } class HarmonyDataProvider extends EventEmitter { - private _harmony: any; private _log: any; - private _hubAddress: string = ""; - private _connected: boolean = false; + private _deviceConfigs: Array; + private _hubs: { [hubName: string]: IHub } = {}; private _devices: { [name: string]: IDevice; } = {}; private _states: { [controlUnitName: string]: (IActivityState | undefined) } = {}; @@ -34,18 +36,13 @@ class HarmonyDataProvider extends EventEmitter { constructor(props: IHarmonyDataProviderProps) { super(); this._log = props.log; - this._hubAddress = props.hubAddress; + //Construct hubs + props.hubs.forEach((value: IHub) => { + value.Harmony = new Harmony(); + this._hubs[value.Name] = value; + }); this._matrix = props.matrix; - - this._harmony = new Harmony(); - - //Listeners - this._harmony.on('open', () => { - this._connected = true; - }); - this._harmony.on('close', () => { - this._connected = false; - }); + this._deviceConfigs = props.deviceConfigs; this.connect(); } @@ -82,8 +79,8 @@ class HarmonyDataProvider extends EventEmitter { devicesToTurnOff = this.sanitizeDeviceList(devicesToTurnOff, controlUnitName); //Turn off devices - devicesToTurnOff.forEach((device: IDevice) => { - this.powerOffDevice(device); + devicesToTurnOff.forEach(async (device: IDevice) => { + await device.powerOff(); }); this._states[controlUnitName] = undefined; @@ -112,7 +109,7 @@ class HarmonyDataProvider extends EventEmitter { if (device && device.name && this._devices[device.name]) { if (!device.on) { this._log(`Turning on device ${device.name}`) - await this.powerOnDevice(device); + await device.powerOn(); } } })); @@ -123,8 +120,7 @@ class HarmonyDataProvider extends EventEmitter { let device: IDevice = this.getDeviceFromName(value.DeviceName); if (device && device.supportsCommand(`Input${value.Input}`)) { - let command: ICommand = device.getCommand(`Input${value.Input}`); - await this.sendCommand(command); + await device.sendCommand(`Input${value.Input}`) } }) ); @@ -141,10 +137,10 @@ class HarmonyDataProvider extends EventEmitter { //Route hdmi if (matrixDevice.supportsCommand(inputCommandName) && matrixDevice.supportsCommand(outputCommandName)) { - await this.sendCommand(matrixDevice.getCommand(outputCommandName)); - await this.sendCommand(matrixDevice.getCommand(inputCommandName)); - await this.sendCommand(matrixDevice.getCommand(outputCommandName)); - await this.sendCommand(matrixDevice.getCommand(inputCommandName)); + await matrixDevice.sendCommand(outputCommandName); + await matrixDevice.sendCommand(inputCommandName); + await matrixDevice.sendCommand(outputCommandName); + await matrixDevice.sendCommand(inputCommandName); } } @@ -175,7 +171,7 @@ class HarmonyDataProvider extends EventEmitter { if (device) { if (device.on) { this._log(`Turning off device ${device.name}`) - await this.powerOffDevice(device); + await device.powerOff(); } } }) @@ -194,9 +190,7 @@ class HarmonyDataProvider extends EventEmitter { let volumeUpCommand: string = "Volume Up" if (this._states[controlUnitName]) { let volumeDevice: IDevice = this.getDeviceFromName(this._states[controlUnitName]!.currentActivity.VolumeDevice); - if (volumeDevice.supportsCommand(volumeUpCommand)) { - this.sendCommand(volumeDevice.getCommand(volumeUpCommand)); - } + await volumeDevice.sendCommand(volumeUpCommand); } } @@ -207,9 +201,7 @@ class HarmonyDataProvider extends EventEmitter { let volumeDownCommand: string = "Volume Down" if (this._states[controlUnitName]) { let volumeDevice: IDevice = this.getDeviceFromName(this._states[controlUnitName]!.currentActivity.VolumeDevice); - if (volumeDevice.supportsCommand(volumeDownCommand)) { - this.sendCommand(volumeDevice.getCommand(volumeDownCommand)); - } + await volumeDevice.sendCommand(volumeDownCommand); } } @@ -263,9 +255,7 @@ class HarmonyDataProvider extends EventEmitter { } } - if (device && device.supportsCommand(commandName)) { - this.sendCommand(device.getCommand(commandName)); - } + await device.sendCommand(commandName); } } @@ -291,40 +281,28 @@ class HarmonyDataProvider extends EventEmitter { } } - /** - * Send a command to the harmony hub. - * @param command The command to send. - */ - public sendCommand = async (command: ICommand) => { - try { - //Execute command - let response = await this._harmony.sendCommand(JSON.stringify(command)); - - //Sleep - await sleep(800); - } catch (err) { - this._log(`ERROR - error sending command to harmony: ${err}`); - } - - } - /** * Connect to harmony and receive device info */ private connect = async () => { - await this._harmony.connect(this._hubAddress); + Object.values(this._hubs).forEach((value: IHub) => { + value.Harmony.connect(value.Ip); + }); let self = this; setTimeout(async function () { - if (self._connected) { - let devices: any = await self._harmony.getDevices(); + Object.keys(self._hubs).forEach(async (key: string) => { + let hub: IHub = self._hubs[key]; + + //Gather devices + let devices: any = await hub.Harmony.getDevices(); try { await Promise.all( //Add each to dictionary devices.map(async (dev: any) => { //get commands let commands: { [name: string]: ICommand } = {}; - let deviceCommands: any = await self._harmony.getDeviceCommands(dev.id); + let deviceCommands: any = await hub.Harmony.getDeviceCommands(dev.id); deviceCommands.forEach((command: any) => { commands[command.label] = command.action; }); @@ -333,6 +311,8 @@ class HarmonyDataProvider extends EventEmitter { name: dev.label, commands: commands, on: false, + harmony: hub.Harmony, + log: self._log, //Define device methods supportsCommand(commandName: string): boolean { let command = commands[commandName]; @@ -340,6 +320,44 @@ class HarmonyDataProvider extends EventEmitter { }, getCommand(commandName: string): ICommand { return commands[commandName]; + }, + 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; + } + }, + 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; + } + }, + async sendCommand(commandName: string): Promise { + let command!: ICommand; + if (this.supportsCommand(commandName)) { + command = this.getCommand(commandName); + } + + try { + //Execute command + await this.harmony.sendCommand(JSON.stringify(command)); + + //Sleep + await sleep(800); + } catch (err) { + this.log(`ERROR - error sending command to harmony: ${err}`); + } } } })); @@ -349,40 +367,13 @@ class HarmonyDataProvider extends EventEmitter { } catch (err) { self._log(`ERROR - error connecting to harmony: ${err}`); } - } + + }); + + }, 1000); } - /** - * Power off a device (Power toggle if no power off). - */ - private powerOffDevice = async (device: IDevice) => { - let powerOffCommand: string = "Power Off"; - let powerToggleCommand: string = "Power Toggle"; - if (device && device.supportsCommand(powerOffCommand)) { - await this.sendCommand(device.getCommand(powerOffCommand)); - device.on = false; - } else if (device && device.supportsCommand(powerToggleCommand)) { - await this.sendCommand(device.getCommand(powerToggleCommand)); - device.on = false; - } - } - - /** - * Power on a device (Power toggle if no power on). - */ - private powerOnDevice = async (device: IDevice) => { - let powerOnCommand: string = "Power On"; - let powerToggleCommand: string = "Power Toggle"; - if (device && device.supportsCommand(powerOnCommand)) { - await this.sendCommand(device.getCommand(powerOnCommand)); - device.on = true; - } else if (device && device.supportsCommand(powerToggleCommand)) { - await this.sendCommand(device.getCommand(powerToggleCommand)); - device.on = true; - } - } - /** * Get the IDevice by name. * @param deviceName The device to retrieve. diff --git a/src/Models/Config/IConfig.ts b/src/Models/Config/IConfig.ts index 6d7929c..2c1e9b0 100644 --- a/src/Models/Config/IConfig.ts +++ b/src/Models/Config/IConfig.ts @@ -1,6 +1,8 @@ import { IMatrix } from "./IMatrix"; import { IActivity } from "./IActivity"; import { IDeviceButton } from "./IDeviceButton"; +import { IDeviceConfig } from "./IDeviceConfig"; +import { IHub } from './IHub'; export interface IControlUnit { DisplayName: string; @@ -9,8 +11,10 @@ export interface IControlUnit { export interface IConfig { hubIp: string; - EmitDevicesOnStartup: boolean, - Matrix: IMatrix - ControlUnits: Array - DeviceButtons: Array + EmitDevicesOnStartup: boolean; + Matrix: IMatrix; + ControlUnits: Array; + DeviceButtons: Array; + Devices: Array; + Hubs: Array; } \ No newline at end of file diff --git a/src/Models/Config/IDeviceButton.ts b/src/Models/Config/IDeviceButton.ts index 2e1795d..d6a3e22 100644 --- a/src/Models/Config/IDeviceButton.ts +++ b/src/Models/Config/IDeviceButton.ts @@ -2,5 +2,6 @@ export interface IDeviceButton { DeviceName: string; ButtonName: string; DisplayName: string; + NumberOfKeyPresses: number; IsStateful: boolean; } \ No newline at end of file diff --git a/src/Models/Config/IDeviceConfig.ts b/src/Models/Config/IDeviceConfig.ts new file mode 100644 index 0000000..97b74a0 --- /dev/null +++ b/src/Models/Config/IDeviceConfig.ts @@ -0,0 +1,4 @@ +export interface IDeviceConfig { + Name: string; + Hub: string; +} \ No newline at end of file diff --git a/src/Models/Config/IHub.ts b/src/Models/Config/IHub.ts new file mode 100644 index 0000000..95b0f45 --- /dev/null +++ b/src/Models/Config/IHub.ts @@ -0,0 +1,5 @@ +export interface IHub { + Name: string; + Ip: string; + Harmony: any; +} \ No newline at end of file diff --git a/src/Models/HarmonyDevice.ts b/src/Models/HarmonyDevice.ts new file mode 100644 index 0000000..01e586f --- /dev/null +++ b/src/Models/HarmonyDevice.ts @@ -0,0 +1,79 @@ +import { ICommand } from "./IDevice"; +import { sleep } from "../Util/Sleep"; + +export interface IHarmonyDeviceProps { + id: string; + name: string; + harmony: any; + log: any; + commands: { [name: string]: ICommand }; +} + +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; + } + + public id: string; + public name: string; + + //Define device methods + public supportsCommand(commandName: string): boolean { + let command = this._commands[commandName]; + return (command) ? true : false; + } + + 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 + await this._harmony.sendCommand(JSON.stringify(command)); + + //Sleep + await sleep(800); + } catch (err) { + this._log(`ERROR - error sending command to harmony: ${err}`); + } + } +} \ No newline at end of file diff --git a/src/Models/IDevice.ts b/src/Models/IDevice.ts index bcede86..eaedc78 100644 --- a/src/Models/IDevice.ts +++ b/src/Models/IDevice.ts @@ -5,10 +5,15 @@ export interface ICommand { } export interface IDevice { - id: string, - name: string, + id: string; + name: string; + harmony: any; + log: any supportsCommand(commandName: string): boolean, getCommand(commandName: string): ICommand, + powerOn(): Promise; + powerOff(): Promise; + sendCommand(commandName: string): Promise; commands: { [name: string]: ICommand }; on: boolean; } \ No newline at end of file From ba1a1685ae4b420383acd945c3f1f91ad38fce77 Mon Sep 17 00:00:00 2001 From: watsonb8 Date: Thu, 23 Jan 2020 09:01:14 -0500 Subject: [PATCH 2/7] Using harmony device Still need to look up devices specific to hubs (In the case of the same device on multiple hubs) --- src/DataProviders/HarmonyDataProvider.ts | 92 ++++++------------------ src/Models/HarmonyDevice.ts | 4 ++ src/index.ts | 3 +- 3 files changed, 29 insertions(+), 70 deletions(-) diff --git a/src/DataProviders/HarmonyDataProvider.ts b/src/DataProviders/HarmonyDataProvider.ts index 7386be3..5d4b7da 100644 --- a/src/DataProviders/HarmonyDataProvider.ts +++ b/src/DataProviders/HarmonyDataProvider.ts @@ -7,6 +7,7 @@ import { EventEmitter } from "events"; import { IDevice, ICommand } from '../Models/IDevice'; import { IHub } from "../Models/Config/IHub"; import { IDeviceConfig } from "../Models/Config/IDeviceConfig"; +import { HarmonyDevice } from "../Models/HarmonyDevice"; let Characteristic: HAPNodeJS.Characteristic; @@ -28,7 +29,7 @@ class HarmonyDataProvider extends EventEmitter { private _deviceConfigs: Array; private _hubs: { [hubName: string]: IHub } = {}; - private _devices: { [name: string]: IDevice; } = {}; + private _devices: { [name: string]: HarmonyDevice; } = {}; private _states: { [controlUnitName: string]: (IActivityState | undefined) } = {}; private _matrix: IMatrix; @@ -47,7 +48,7 @@ class HarmonyDataProvider extends EventEmitter { this.connect(); } - public get devices(): { [name: string]: IDevice; } { + public get devices(): { [name: string]: HarmonyDevice; } { return this._devices; } @@ -70,8 +71,8 @@ class HarmonyDataProvider extends EventEmitter { return; } //Build potential list of devices to turn off - let devicesToTurnOff: Array = this._states[controlUnitName]!.currentActivity.DeviceSetupList - .map((value: IDeviceSetupItem): IDevice => { + let devicesToTurnOff: Array = this._states[controlUnitName]!.currentActivity.DeviceSetupList + .map((value: IDeviceSetupItem): HarmonyDevice => { return this.getDeviceFromName(value.DeviceName); }); @@ -79,7 +80,7 @@ class HarmonyDataProvider extends EventEmitter { devicesToTurnOff = this.sanitizeDeviceList(devicesToTurnOff, controlUnitName); //Turn off devices - devicesToTurnOff.forEach(async (device: IDevice) => { + devicesToTurnOff.forEach(async (device: HarmonyDevice) => { await device.powerOff(); }); @@ -97,7 +98,7 @@ class HarmonyDataProvider extends EventEmitter { } //Build potential list of devices to to turn on - let devicesToTurnOn: Array = activity.DeviceSetupList.map((value: IDeviceSetupItem): IDevice => { + let devicesToTurnOn: Array = activity.DeviceSetupList.map((value: IDeviceSetupItem): HarmonyDevice => { return this.getDeviceFromName(value.DeviceName); }); @@ -105,7 +106,7 @@ class HarmonyDataProvider extends EventEmitter { devicesToTurnOn = this.sanitizeDeviceList(devicesToTurnOn, controlUnitName); //Turn on devices - await Promise.all(devicesToTurnOn.map(async (device: IDevice) => { + await Promise.all(devicesToTurnOn.map(async (device: HarmonyDevice) => { if (device && device.name && this._devices[device.name]) { if (!device.on) { this._log(`Turning on device ${device.name}`) @@ -117,7 +118,7 @@ class HarmonyDataProvider extends EventEmitter { //Assign correct input await Promise.all( activity.DeviceSetupList.map(async (value: IDeviceSetupItem) => { - let device: IDevice = this.getDeviceFromName(value.DeviceName); + let device: HarmonyDevice = this.getDeviceFromName(value.DeviceName); if (device && device.supportsCommand(`Input${value.Input}`)) { await device.sendCommand(`Input${value.Input}`) @@ -133,7 +134,7 @@ class HarmonyDataProvider extends EventEmitter { let inputCommandName: string = `In ${input.InputNumber}`; let outputCommandName: string = `Out ${output.OutputLetter}`; - let matrixDevice: IDevice = this.getDeviceFromName(this._matrix.DeviceName); + let matrixDevice: HarmonyDevice = this.getDeviceFromName(this._matrix.DeviceName); //Route hdmi if (matrixDevice.supportsCommand(inputCommandName) && matrixDevice.supportsCommand(outputCommandName)) { @@ -146,13 +147,13 @@ class HarmonyDataProvider extends EventEmitter { //Build potential list of devices to turn off if (lastActivity) { - let devicesToTurnOff: Array = lastActivity.DeviceSetupList.map((value: IDeviceSetupItem): IDevice => { + 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: IDevice, index: number) => { + devicesToTurnOff.forEach((device: HarmonyDevice, index: number) => { if (device && device.name && activity.DeviceSetupList.some(e => { return (e && e.DeviceName === device.name) })) { @@ -167,7 +168,7 @@ class HarmonyDataProvider extends EventEmitter { await Promise.all( //Turn off devices - devicesToTurnOff.map(async (device: IDevice) => { + devicesToTurnOff.map(async (device: HarmonyDevice) => { if (device) { if (device.on) { this._log(`Turning off device ${device.name}`) @@ -189,7 +190,7 @@ class HarmonyDataProvider extends EventEmitter { public volumeUp = async (controlUnitName: string) => { let volumeUpCommand: string = "Volume Up" if (this._states[controlUnitName]) { - let volumeDevice: IDevice = this.getDeviceFromName(this._states[controlUnitName]!.currentActivity.VolumeDevice); + let volumeDevice: HarmonyDevice = this.getDeviceFromName(this._states[controlUnitName]!.currentActivity.VolumeDevice); await volumeDevice.sendCommand(volumeUpCommand); } } @@ -200,7 +201,7 @@ class HarmonyDataProvider extends EventEmitter { public volumeDown = async (controlUnitName: string) => { let volumeDownCommand: string = "Volume Down" if (this._states[controlUnitName]) { - let volumeDevice: IDevice = this.getDeviceFromName(this._states[controlUnitName]!.currentActivity.VolumeDevice); + let volumeDevice: HarmonyDevice = this.getDeviceFromName(this._states[controlUnitName]!.currentActivity.VolumeDevice); await volumeDevice.sendCommand(volumeDownCommand); } } @@ -215,7 +216,7 @@ class HarmonyDataProvider extends EventEmitter { if (this._states[controlUnitName]) { let commandName: string = ""; - let device: IDevice = this.getDeviceFromName(this._states[controlUnitName]!.currentActivity.ControlDevice); + let device: HarmonyDevice = this.getDeviceFromName(this._states[controlUnitName]!.currentActivity.ControlDevice); switch (key) { case RemoteKey.ARROW_UP: { commandName = "Direction Up"; @@ -273,7 +274,7 @@ class HarmonyDataProvider extends EventEmitter { * @param deviceName The device name */ public getCommand(deviceCommandName: string, deviceName: string): ICommand | undefined { - const device: IDevice = this.getDeviceFromName(deviceName); + const device: HarmonyDevice = this.getDeviceFromName(deviceName); if (device && device.supportsCommand(deviceCommandName)) { return device.getCommand(deviceCommandName); } else { @@ -306,60 +307,13 @@ class HarmonyDataProvider extends EventEmitter { deviceCommands.forEach((command: any) => { commands[command.label] = command.action; }); - self._devices[dev.label] = { + self._devices[dev.label] = new HarmonyDevice({ id: dev.id, name: dev.label, commands: commands, - on: false, - harmony: hub.Harmony, log: self._log, - //Define device methods - supportsCommand(commandName: string): boolean { - let command = commands[commandName]; - return (command) ? true : false; - }, - getCommand(commandName: string): ICommand { - return commands[commandName]; - }, - 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; - } - }, - 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; - } - }, - async sendCommand(commandName: string): Promise { - let command!: ICommand; - if (this.supportsCommand(commandName)) { - command = this.getCommand(commandName); - } - - try { - //Execute command - await this.harmony.sendCommand(JSON.stringify(command)); - - //Sleep - await sleep(800); - } catch (err) { - this.log(`ERROR - error sending command to harmony: ${err}`); - } - } - } + harmony: hub.Harmony + }); })); self._log(`Harmony data provider ready`); self.emit("Ready"); @@ -378,7 +332,7 @@ class HarmonyDataProvider extends EventEmitter { * Get the IDevice by name. * @param deviceName The device to retrieve. */ - private getDeviceFromName(deviceName: string): IDevice { + private getDeviceFromName(deviceName: string): HarmonyDevice { return this._devices[deviceName]; } @@ -387,7 +341,7 @@ class HarmonyDataProvider extends EventEmitter { * @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 { + private sanitizeDeviceList(devicesToTurnOn: Array, controlUnitName: string): Array { for (let controlUnitKey in this._states) { //Skip self if (controlUnitKey === controlUnitName) { @@ -399,7 +353,7 @@ class HarmonyDataProvider extends EventEmitter { currentOtherState.currentActivity.DeviceSetupList.forEach((value: IDeviceSetupItem) => { //there are devices to remove if (devicesToTurnOn.some(e => e && e.name === value.DeviceName)) { - let deviceToRemove: IDevice = devicesToTurnOn.filter(i => i.name === value.DeviceName)[0]; + let deviceToRemove: HarmonyDevice = devicesToTurnOn.filter(i => i.name === value.DeviceName)[0]; delete devicesToTurnOn[devicesToTurnOn.indexOf(deviceToRemove)]; } }); diff --git a/src/Models/HarmonyDevice.ts b/src/Models/HarmonyDevice.ts index 01e586f..614c414 100644 --- a/src/Models/HarmonyDevice.ts +++ b/src/Models/HarmonyDevice.ts @@ -26,6 +26,10 @@ export class HarmonyDevice { public id: string; public name: string; + public get on(): boolean { + return this._on; + } + //Define device methods public supportsCommand(commandName: string): boolean { let command = this._commands[commandName]; diff --git a/src/index.ts b/src/index.ts index 2526c17..9021fc2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,7 +40,8 @@ class HarmonyMatrixPlatform { if (this.config) { //construct data provider this.dataProvider = new HarmonyDataProvider({ - hubAddress: this.config.hubIp, + hubs: this.config.Hubs, + deviceConfigs: this.config.Devices, matrix: this.config.Matrix, log: this.log }); From ec0c857c97053c39d6def1d64b8750c038836940 Mon Sep 17 00:00:00 2001 From: watsonb8 Date: Thu, 23 Jan 2020 09:28:59 -0500 Subject: [PATCH 3/7] First pass done but untested --- src/DataProviders/HarmonyDataProvider.ts | 21 ++++++++++++--------- src/Models/HarmonyDevice.ts | 4 ++++ src/index.ts | 18 +++++++++++++----- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/DataProviders/HarmonyDataProvider.ts b/src/DataProviders/HarmonyDataProvider.ts index 5d4b7da..cf81997 100644 --- a/src/DataProviders/HarmonyDataProvider.ts +++ b/src/DataProviders/HarmonyDataProvider.ts @@ -26,10 +26,10 @@ interface IHarmonyDataProviderProps { class HarmonyDataProvider extends EventEmitter { private _log: any; - private _deviceConfigs: Array; + private _hubsByDevice: { [deviceName: string]: string } = {}; private _hubs: { [hubName: string]: IHub } = {}; - - private _devices: { [name: string]: HarmonyDevice; } = {}; + private _devicesByHub: { [hubName: string]: { [deviceName: string]: HarmonyDevice } } = {}; + // private _devices: { [name: string]: HarmonyDevice; } = {}; private _states: { [controlUnitName: string]: (IActivityState | undefined) } = {}; private _matrix: IMatrix; @@ -43,13 +43,16 @@ class HarmonyDataProvider extends EventEmitter { this._hubs[value.Name] = value; }); this._matrix = props.matrix; - this._deviceConfigs = props.deviceConfigs; + props.deviceConfigs.forEach((deviceConfig: IDeviceConfig) => { + this._hubsByDevice[deviceConfig.Name] = deviceConfig.Hub; + }); + // this._deviceConfigs = props.deviceConfigs; this.connect(); } - public get devices(): { [name: string]: HarmonyDevice; } { - return this._devices; + public get devicesByHub(): { [hubName: string]: { [deviceName: string]: HarmonyDevice } } { + return this._devicesByHub; } /** @@ -107,7 +110,7 @@ class HarmonyDataProvider extends EventEmitter { //Turn on devices await Promise.all(devicesToTurnOn.map(async (device: HarmonyDevice) => { - if (device && device.name && this._devices[device.name]) { + if (device && device.name) { if (!device.on) { this._log(`Turning on device ${device.name}`) await device.powerOn(); @@ -307,7 +310,7 @@ class HarmonyDataProvider extends EventEmitter { deviceCommands.forEach((command: any) => { commands[command.label] = command.action; }); - self._devices[dev.label] = new HarmonyDevice({ + self._devicesByHub[hub.Name][dev.label] = new HarmonyDevice({ id: dev.id, name: dev.label, commands: commands, @@ -333,7 +336,7 @@ class HarmonyDataProvider extends EventEmitter { * @param deviceName The device to retrieve. */ private getDeviceFromName(deviceName: string): HarmonyDevice { - return this._devices[deviceName]; + return this._devicesByHub[this._hubsByDevice[deviceName]][deviceName]; } /** diff --git a/src/Models/HarmonyDevice.ts b/src/Models/HarmonyDevice.ts index 614c414..9a321d7 100644 --- a/src/Models/HarmonyDevice.ts +++ b/src/Models/HarmonyDevice.ts @@ -30,6 +30,10 @@ export class HarmonyDevice { return this._on; } + public get commands(): { [name: string]: ICommand } { + return this._commands; + } + //Define device methods public supportsCommand(commandName: string): boolean { let command = this._commands[commandName]; diff --git a/src/index.ts b/src/index.ts index 9021fc2..59a2ec5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,6 +2,7 @@ import * as Accessories from "./Accessories"; import HarmonyDataProvider from "./DataProviders/HarmonyDataProvider"; import * as Config from "./Models/Config"; import { IDevice } from "./Models"; +import { HarmonyDevice } from "./Models/HarmonyDevice"; let Accessory: any; let Homebridge: any; @@ -49,13 +50,20 @@ class HarmonyMatrixPlatform { //Emit devices if requested if (this.config.EmitDevicesOnStartup) { this.dataProvider.on("Ready", () => { - const devices: { [name: string]: IDevice } = this.dataProvider!.devices; - Object.values(devices).forEach((device: IDevice) => { - this.log(`${device.name} : ${device.id}`); - Object.keys(device.commands).forEach((command: string) => { - this.log(` ${command}`); + + const hubs: { [hubName: string]: { [deviceName: string]: HarmonyDevice } } = this.dataProvider!.devicesByHub; + Object.keys(hubs).forEach((hubName: string) => { + const deviceDictionary = hubs[hubName]; + this.log(`${hubName}`) + + Object.values(deviceDictionary).forEach((device: HarmonyDevice) => { + this.log(` ${device.name} : ${device.id}`); + Object.keys(device.commands).forEach((command: string) => { + this.log(` ${command}`); + }); }); }); + }); } } From 97c0f625db9864bfc59bf76dea3135ad3ed14bad Mon Sep 17 00:00:00 2001 From: watsonb8 Date: Fri, 24 Jan 2020 21:31:43 -0500 Subject: [PATCH 4/7] Successful multi hub --- src/Accessories/DeviceButton.ts | 4 +- src/DataProviders/HarmonyDataProvider.ts | 89 ++++++++---------------- src/Models/HarmonyHub.ts | 63 +++++++++++++++++ src/index.ts | 22 +++--- 4 files changed, 106 insertions(+), 72 deletions(-) create mode 100644 src/Models/HarmonyHub.ts diff --git a/src/Accessories/DeviceButton.ts b/src/Accessories/DeviceButton.ts index cbed4c3..dc01b53 100644 --- a/src/Accessories/DeviceButton.ts +++ b/src/Accessories/DeviceButton.ts @@ -113,11 +113,11 @@ export class DeviceButton implements IAccessory { //change state if stateful if (this._buttonInfo.IsStateful && this._buttonState != newState) { this._buttonState = newState - await this._dataProvider.sendCommand(this._deviceCommand); + //TODO await this._dataProvider.sendCommand(this._deviceCommand); } else if (!this._buttonInfo.IsStateful) { //Send the number of configured key presses for (let i = 0; i < this._buttonInfo.NumberOfKeyPresses; i++) { - await this._dataProvider.sendCommand(this._deviceCommand); + //TODO await this._dataProvider.sendCommand(this._deviceCommand); } this._switchService.getCharacteristic(Characteristic.On).updateValue(false); return callback(new Error("Normal Response")); diff --git a/src/DataProviders/HarmonyDataProvider.ts b/src/DataProviders/HarmonyDataProvider.ts index cf81997..b4872f1 100644 --- a/src/DataProviders/HarmonyDataProvider.ts +++ b/src/DataProviders/HarmonyDataProvider.ts @@ -2,12 +2,12 @@ import { IActivity } from "../Models/Config/IActivity"; import { IDeviceSetupItem } from "../Models/Config/IDeviceSetupItem"; import { IInput, IMatrix, IOutput } from "../Models/Config/IMatrix"; import { RemoteKey } from '../Accessories/ControlUnit'; -import { sleep } from '../Util/Sleep'; import { EventEmitter } from "events"; -import { IDevice, ICommand } from '../Models/IDevice'; +import { ICommand } from '../Models/IDevice'; import { IHub } from "../Models/Config/IHub"; import { IDeviceConfig } from "../Models/Config/IDeviceConfig"; import { HarmonyDevice } from "../Models/HarmonyDevice"; +import { HarmonyHub } from "../Models/HarmonyHub"; let Characteristic: HAPNodeJS.Characteristic; @@ -27,9 +27,8 @@ interface IHarmonyDataProviderProps { class HarmonyDataProvider extends EventEmitter { private _log: any; private _hubsByDevice: { [deviceName: string]: string } = {}; - private _hubs: { [hubName: string]: IHub } = {}; - private _devicesByHub: { [hubName: string]: { [deviceName: string]: HarmonyDevice } } = {}; - // private _devices: { [name: string]: HarmonyDevice; } = {}; + private _hubs: { [hubName: string]: HarmonyHub } = {}; + // private _devicesByHub: { [hubName: string]: { [deviceName: string]: HarmonyDevice } } = {}; private _states: { [controlUnitName: string]: (IActivityState | undefined) } = {}; private _matrix: IMatrix; @@ -37,23 +36,18 @@ class HarmonyDataProvider extends EventEmitter { constructor(props: IHarmonyDataProviderProps) { super(); this._log = props.log; - //Construct hubs - props.hubs.forEach((value: IHub) => { - value.Harmony = new Harmony(); - this._hubs[value.Name] = value; - }); this._matrix = props.matrix; props.deviceConfigs.forEach((deviceConfig: IDeviceConfig) => { this._hubsByDevice[deviceConfig.Name] = deviceConfig.Hub; }); // this._deviceConfigs = props.deviceConfigs; - this.connect(); + this.connect(props.hubs); } - public get devicesByHub(): { [hubName: string]: { [deviceName: string]: HarmonyDevice } } { - return this._devicesByHub; - } + // public get devicesByHub(): { [hubName: string]: { [deviceName: string]: HarmonyDevice } } { + // return this._devicesByHub; + // } /** * Power on all devices in an activity. @@ -285,50 +279,20 @@ class HarmonyDataProvider extends EventEmitter { } } - /** - * Connect to harmony and receive device info - */ - private connect = async () => { - Object.values(this._hubs).forEach((value: IHub) => { - value.Harmony.connect(value.Ip); - }); - let self = this; - - setTimeout(async function () { - Object.keys(self._hubs).forEach(async (key: string) => { - let hub: IHub = self._hubs[key]; - - //Gather devices - let devices: any = await hub.Harmony.getDevices(); - try { - await Promise.all( - //Add each to dictionary - devices.map(async (dev: any) => { - //get commands - let commands: { [name: string]: ICommand } = {}; - let deviceCommands: any = await hub.Harmony.getDeviceCommands(dev.id); - deviceCommands.forEach((command: any) => { - commands[command.label] = command.action; - }); - self._devicesByHub[hub.Name][dev.label] = new HarmonyDevice({ - id: dev.id, - name: dev.label, - commands: commands, - log: self._log, - harmony: hub.Harmony - }); - })); - self._log(`Harmony data provider ready`); - self.emit("Ready"); - - } catch (err) { - self._log(`ERROR - error connecting to harmony: ${err}`); - } - - }); - - - }, 1000); + private connect = async (hubs: Array) => { + for (let i = 0; i < hubs.length; i++) { + const hub = hubs[i]; + const newHarmonyHub = new HarmonyHub(hub.Ip, this._log); + this._hubs[hub.Name] = newHarmonyHub; + await newHarmonyHub.initialize(); + } + // await Promise.all( + // hubs.map(async (hub: IHub): Promise => { + // const newHarmonyHub = new HarmonyHub(hub.Ip, this._log); + // this._hubs[hub.Name] = newHarmonyHub; + // await newHarmonyHub.initialize(); + // }) + // ) } /** @@ -336,7 +300,14 @@ class HarmonyDataProvider extends EventEmitter { * @param deviceName The device to retrieve. */ private getDeviceFromName(deviceName: string): HarmonyDevice { - return this._devicesByHub[this._hubsByDevice[deviceName]][deviceName]; + let device: HarmonyDevice | undefined; + try { + device = this._hubs[this._hubsByDevice[deviceName]].getDeviceByName(deviceName); + } catch (err) { + this._log(`Error retrieving device from hub: ${err}`); + } + + return device!; } /** diff --git a/src/Models/HarmonyHub.ts b/src/Models/HarmonyHub.ts new file mode 100644 index 0000000..048e200 --- /dev/null +++ b/src/Models/HarmonyHub.ts @@ -0,0 +1,63 @@ +import { HarmonyDevice } from './HarmonyDevice'; +const Harmony = require("harmony-websocket"); +import { ICommand } from './IDevice'; +import { EventEmitter } from 'events'; + + + +export class HarmonyHub extends EventEmitter { + private _devices: { [deviceName: string]: HarmonyDevice } = {} + private _ip: string; + private _harmony: any; + private _log: any; + + constructor(ipAddress: string, log: any) { + super(); + this._ip = ipAddress; + this._log = log; + } + + public get devices(): { [deviceName: string]: HarmonyDevice } { + return this._devices; + } + + public getDeviceByName = (deviceName: string): HarmonyDevice => { + return this._devices[deviceName]; + } + + public initialize = async () => { + this._harmony = new Harmony(); + await this._harmony.connect(this._ip); + + //Gather devices + let devices: any = await this._harmony.getDevices(); + try { + await Promise.all( + //Add each to dictionary + devices.map(async (dev: any) => { + //get commands + let commands: { [name: string]: ICommand } = {}; + let deviceCommands: any = await this._harmony.getDeviceCommands(dev.id); + deviceCommands.forEach((command: any) => { + commands[command.label] = command.action; + }); + this._devices[dev.label] = new HarmonyDevice({ + id: dev.id, + name: dev.label, + commands: commands, + log: this._log, + harmony: this._harmony + }); + })); + this._log(`Harmony data provider ready`); + this.emit("Ready"); + + } catch (err) { + this._log(`ERROR - error connecting to harmony: ${err}`); + } + } + + private connect = async (): Promise => { + await this._harmony.Connect(this._ip); + } +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 59a2ec5..ace114a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -51,18 +51,18 @@ class HarmonyMatrixPlatform { if (this.config.EmitDevicesOnStartup) { this.dataProvider.on("Ready", () => { - const hubs: { [hubName: string]: { [deviceName: string]: HarmonyDevice } } = this.dataProvider!.devicesByHub; - Object.keys(hubs).forEach((hubName: string) => { - const deviceDictionary = hubs[hubName]; - this.log(`${hubName}`) + // TODO const hubs: { [hubName: string]: { [deviceName: string]: HarmonyDevice } } = this.dataProvider!.devicesByHub; + // Object.keys(hubs).forEach((hubName: string) => { + // const deviceDictionary = hubs[hubName]; + // this.log(`${hubName}`) - Object.values(deviceDictionary).forEach((device: HarmonyDevice) => { - this.log(` ${device.name} : ${device.id}`); - Object.keys(device.commands).forEach((command: string) => { - this.log(` ${command}`); - }); - }); - }); + // Object.values(deviceDictionary).forEach((device: HarmonyDevice) => { + // this.log(` ${device.name} : ${device.id}`); + // Object.keys(device.commands).forEach((command: string) => { + // this.log(` ${command}`); + // }); + // }); + // }); }); } From 6f7fc4c2afad9c73bb021f4c10137b5b16581d14 Mon Sep 17 00:00:00 2001 From: watsonb8 Date: Fri, 24 Jan 2020 21:47:13 -0500 Subject: [PATCH 5/7] Fixed device buttons --- src/Accessories/DeviceButton.ts | 16 +++--- src/DataProviders/HarmonyDataProvider.ts | 66 +++++++++++++----------- src/Models/HarmonyHub.ts | 8 ++- src/index.ts | 23 +++++---- 4 files changed, 61 insertions(+), 52 deletions(-) diff --git a/src/Accessories/DeviceButton.ts b/src/Accessories/DeviceButton.ts index dc01b53..d37c4fa 100644 --- a/src/Accessories/DeviceButton.ts +++ b/src/Accessories/DeviceButton.ts @@ -2,6 +2,7 @@ import HarmonyDataProvider from "../DataProviders/HarmonyDataProvider"; import { IDeviceButton } from "../Models/Config"; import { IAccessory } from "./IAccessory"; import { ICommand } from "../Models"; +import { HarmonyDevice } from "../Models/HarmonyDevice"; let Service: HAPNodeJS.Service; let Characteristic: HAPNodeJS.Characteristic; @@ -29,7 +30,7 @@ export class DeviceButton implements IAccessory { private _dataProvider: HarmonyDataProvider; - private _deviceCommand?: ICommand; + private _device!: HarmonyDevice; private _buttonState: boolean; @@ -101,23 +102,20 @@ export class DeviceButton implements IAccessory { } //Get device command if we don't have it - if (!this._deviceCommand) { - let cmd = this._dataProvider.getCommand(this._buttonInfo.ButtonName, this._buttonInfo.DeviceName); - if (cmd) { - this._deviceCommand = cmd; - } + if (!this._device) { + this._device = this._dataProvider.getDeviceFromName(this._buttonInfo.DeviceName); } //Execute command - if (this._deviceCommand) { + if (this._device) { //change state if stateful if (this._buttonInfo.IsStateful && this._buttonState != newState) { this._buttonState = newState - //TODO await this._dataProvider.sendCommand(this._deviceCommand); + await this._device.sendCommand(this._buttonInfo.ButtonName); } else if (!this._buttonInfo.IsStateful) { //Send the number of configured key presses for (let i = 0; i < this._buttonInfo.NumberOfKeyPresses; i++) { - //TODO await this._dataProvider.sendCommand(this._deviceCommand); + await this._device.sendCommand(this._buttonInfo.ButtonName); } this._switchService.getCharacteristic(Characteristic.On).updateValue(false); return callback(new Error("Normal Response")); diff --git a/src/DataProviders/HarmonyDataProvider.ts b/src/DataProviders/HarmonyDataProvider.ts index b4872f1..422454f 100644 --- a/src/DataProviders/HarmonyDataProvider.ts +++ b/src/DataProviders/HarmonyDataProvider.ts @@ -49,6 +49,10 @@ class HarmonyDataProvider extends EventEmitter { // return this._devicesByHub; // } + public get hubs(): { [hubName: string]: HarmonyHub } { + return this._hubs; + } + /** * Power on all devices in an activity. */ @@ -265,41 +269,11 @@ class HarmonyDataProvider extends EventEmitter { return this._states[controlUnitName] ? this._states[controlUnitName]!.currentActivity : undefined; } - /** - * 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) => { - for (let i = 0; i < hubs.length; i++) { - const hub = hubs[i]; - const newHarmonyHub = new HarmonyHub(hub.Ip, this._log); - this._hubs[hub.Name] = newHarmonyHub; - await newHarmonyHub.initialize(); - } - // await Promise.all( - // hubs.map(async (hub: IHub): Promise => { - // const newHarmonyHub = new HarmonyHub(hub.Ip, this._log); - // this._hubs[hub.Name] = newHarmonyHub; - // await newHarmonyHub.initialize(); - // }) - // ) - } - /** * Get the IDevice by name. * @param deviceName The device to retrieve. */ - private getDeviceFromName(deviceName: string): HarmonyDevice { + public getDeviceFromName(deviceName: string): HarmonyDevice { let device: HarmonyDevice | undefined; try { device = this._hubs[this._hubsByDevice[deviceName]].getDeviceByName(deviceName); @@ -310,6 +284,36 @@ 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) => { + // for (let i = 0; i < hubs.length; i++) { + // const hub = hubs[i]; + // const newHarmonyHub = new HarmonyHub(hub.Ip, this._log); + // this._hubs[hub.Name] = newHarmonyHub; + // await newHarmonyHub.initialize(); + // } + await Promise.all( + hubs.map(async (hub: IHub): Promise => { + const newHarmonyHub = new HarmonyHub(hub.Name, hub.Ip, this._log); + this._hubs[hub.Name] = newHarmonyHub; + await newHarmonyHub.initialize(); + }) + ) + } + /** * Helper function to make sure no control unit depends on device list. * @param devicesToTurnOn The list of devices to modify. diff --git a/src/Models/HarmonyHub.ts b/src/Models/HarmonyHub.ts index 048e200..a78a904 100644 --- a/src/Models/HarmonyHub.ts +++ b/src/Models/HarmonyHub.ts @@ -10,17 +10,23 @@ export class HarmonyHub extends EventEmitter { private _ip: string; private _harmony: any; private _log: any; + private _name: string; - constructor(ipAddress: string, log: any) { + constructor(hubName: string, ipAddress: string, log: any) { super(); this._ip = ipAddress; this._log = log; + this._name = hubName; } public get devices(): { [deviceName: string]: HarmonyDevice } { return this._devices; } + public get hubName(): string { + return this._name; + } + public getDeviceByName = (deviceName: string): HarmonyDevice => { return this._devices[deviceName]; } diff --git a/src/index.ts b/src/index.ts index ace114a..01e960d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,7 @@ import HarmonyDataProvider from "./DataProviders/HarmonyDataProvider"; import * as Config from "./Models/Config"; import { IDevice } from "./Models"; import { HarmonyDevice } from "./Models/HarmonyDevice"; +import { HarmonyHub } from "./Models/HarmonyHub"; let Accessory: any; let Homebridge: any; @@ -51,18 +52,18 @@ class HarmonyMatrixPlatform { if (this.config.EmitDevicesOnStartup) { this.dataProvider.on("Ready", () => { - // TODO const hubs: { [hubName: string]: { [deviceName: string]: HarmonyDevice } } = this.dataProvider!.devicesByHub; - // Object.keys(hubs).forEach((hubName: string) => { - // const deviceDictionary = hubs[hubName]; - // this.log(`${hubName}`) + const hubs = this.dataProvider!.hubs; + Object.values(hubs).forEach((hub: HarmonyHub) => { + const deviceDictionary = hub.devices; + this.log(`${hub.hubName}`) - // Object.values(deviceDictionary).forEach((device: HarmonyDevice) => { - // this.log(` ${device.name} : ${device.id}`); - // Object.keys(device.commands).forEach((command: string) => { - // this.log(` ${command}`); - // }); - // }); - // }); + Object.values(deviceDictionary).forEach((device: HarmonyDevice) => { + this.log(` ${device.name} : ${device.id}`); + Object.keys(device.commands).forEach((command: string) => { + this.log(` ${command}`); + }); + }); + }); }); } From 3ac6ffb8a32dce21c84ad7fa673cb2710259ac89 Mon Sep 17 00:00:00 2001 From: watsonb8 Date: Fri, 24 Jan 2020 23:17:33 -0500 Subject: [PATCH 6/7] Worked around bug where receiver was not turning off --- src/DataProviders/HarmonyDataProvider.ts | 17 ++++++++++------- src/Models/HarmonyDevice.ts | 7 +++++++ src/Models/HarmonyHub.ts | 1 - src/index.ts | 9 +++++---- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/DataProviders/HarmonyDataProvider.ts b/src/DataProviders/HarmonyDataProvider.ts index 422454f..592ab41 100644 --- a/src/DataProviders/HarmonyDataProvider.ts +++ b/src/DataProviders/HarmonyDataProvider.ts @@ -82,7 +82,9 @@ class HarmonyDataProvider extends EventEmitter { //Turn off devices devicesToTurnOff.forEach(async (device: HarmonyDevice) => { - await device.powerOff(); + if (device) { + await device.powerOff(); + } }); this._states[controlUnitName] = undefined; @@ -299,16 +301,17 @@ class HarmonyDataProvider extends EventEmitter { // } private connect = async (hubs: Array) => { - // for (let i = 0; i < hubs.length; i++) { - // const hub = hubs[i]; - // const newHarmonyHub = new HarmonyHub(hub.Ip, this._log); - // this._hubs[hub.Name] = newHarmonyHub; - // await newHarmonyHub.initialize(); - // } + let readyCount = 0; await Promise.all( hubs.map(async (hub: IHub): Promise => { const newHarmonyHub = new HarmonyHub(hub.Name, hub.Ip, this._log); this._hubs[hub.Name] = newHarmonyHub; + newHarmonyHub.on("Ready", () => { + readyCount++; + if (readyCount === Object.keys(this._hubs).length) { + this.emit("Ready"); + } + }) await newHarmonyHub.initialize(); }) ) diff --git a/src/Models/HarmonyDevice.ts b/src/Models/HarmonyDevice.ts index 9a321d7..cecece4 100644 --- a/src/Models/HarmonyDevice.ts +++ b/src/Models/HarmonyDevice.ts @@ -76,6 +76,13 @@ export class HarmonyDevice { 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 diff --git a/src/Models/HarmonyHub.ts b/src/Models/HarmonyHub.ts index a78a904..8e4ce88 100644 --- a/src/Models/HarmonyHub.ts +++ b/src/Models/HarmonyHub.ts @@ -55,7 +55,6 @@ export class HarmonyHub extends EventEmitter { harmony: this._harmony }); })); - this._log(`Harmony data provider ready`); this.emit("Ready"); } catch (err) { diff --git a/src/index.ts b/src/index.ts index 01e960d..9a904c4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -49,9 +49,10 @@ class HarmonyMatrixPlatform { }); //Emit devices if requested - if (this.config.EmitDevicesOnStartup) { - this.dataProvider.on("Ready", () => { + this.dataProvider.on("Ready", () => { + this.log("All hubs connected"); + if (this.config.EmitDevicesOnStartup) { const hubs = this.dataProvider!.hubs; Object.values(hubs).forEach((hub: HarmonyHub) => { const deviceDictionary = hub.devices; @@ -64,9 +65,9 @@ class HarmonyMatrixPlatform { }); }); }); + } + }); - }); - } } } From 7907497360ade20dc4d514c3ff72e1d126f1cea4 Mon Sep 17 00:00:00 2001 From: watsonb8 Date: Fri, 24 Jan 2020 23:18:01 -0500 Subject: [PATCH 7/7] Bump rev --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0064c3c..aa217fd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "homebridge-harmony-control", - "version": "1.0.6", + "version": "1.0.7", "description": "Homebridge platform to control smart home equipment by room.", "main": "bin/index.js", "scripts": {