Compare commits
	
		
			4 Commits
		
	
	
		
			1.0.7
			...
			feature/ne
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | ed0b418b4d | ||
|  | 2475aea1c8 | ||
|  | 31c5c5b9e2 | ||
|  | a539287b9e | 
							
								
								
									
										87
									
								
								config.json
									
									
									
									
									
								
							
							
						
						
									
										87
									
								
								config.json
									
									
									
									
									
								
							| @@ -1,7 +1,7 @@ | ||||
| { | ||||
|     "bridge": { | ||||
|         "name": "Homebridge", | ||||
|         "username": "CC:22:3D:E3:CE:74", | ||||
|         "username": "CC:22:3D:E3:CE:42", | ||||
|         "port": 51826, | ||||
|         "pin": "031-45-154" | ||||
|     }, | ||||
| @@ -13,8 +13,10 @@ | ||||
|         }, | ||||
|         { | ||||
|             "platform": "HarmonyHubMatrix", | ||||
|             "hubIp": "192.168.1.14", | ||||
|             "harmonyHubIp": "192.168.1.14", | ||||
|             "neeoHubIp": "192.168.1.24", | ||||
|             "Matrix": { | ||||
|                 "Hub": "Harmony", | ||||
|                 "DeviceName": "Gefen AV Switch", | ||||
|                 "Inputs": [ | ||||
|                     { | ||||
| @@ -57,14 +59,17 @@ | ||||
|                             "DisplayName": "Chromecast", | ||||
|                             "DeviceSetupList": [ | ||||
|                                 { | ||||
|                                     "Hub": "Harmony", | ||||
|                                     "DeviceName": "LG TV", | ||||
|                                     "Input": "Hdmi1" | ||||
|                                     "Input": "HDMI1" | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "Hub": "Harmony", | ||||
|                                     "DeviceName": "JVC AV Receiver", | ||||
|                                     "Input": "Dvd" | ||||
|                                     "Input": "DVD" | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "Hub": "Harmony", | ||||
|                                     "DeviceName": "Chromecast", | ||||
|                                     "Input": [] | ||||
|                                 } | ||||
| @@ -78,16 +83,19 @@ | ||||
|                             "DisplayName": "Play Xbox One", | ||||
|                             "DeviceSetupList": [ | ||||
|                                 { | ||||
|                                     "Hub": "Harmony", | ||||
|                                     "DeviceName": "Microsoft Xbox One", | ||||
|                                     "Input": [] | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "Hub": "Harmony", | ||||
|                                     "DeviceName": "LG TV", | ||||
|                                     "Input": "Hdmi1" | ||||
|                                     "Input": "HDMI1" | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "Hub": "Harmony", | ||||
|                                     "DeviceName": "JVC AV Receiver", | ||||
|                                     "Input": "Dvd" | ||||
|                                     "Input": "DVD" | ||||
|                                 } | ||||
|                             ], | ||||
|                             "VolumeDevice": "JVC AV Receiver", | ||||
| @@ -99,16 +107,19 @@ | ||||
|                             "DisplayName": "Play PS4", | ||||
|                             "DeviceSetupList": [ | ||||
|                                 { | ||||
|                                     "Hub": "Harmony", | ||||
|                                     "DeviceName": "Sony PS4", | ||||
|                                     "Input": [] | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "Hub": "Harmony", | ||||
|                                     "DeviceName": "LG TV", | ||||
|                                     "Input": "Hdmi1" | ||||
|                                     "Input": "HDMI1" | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "Hub": "Harmony", | ||||
|                                     "DeviceName": "JVC AV Receiver", | ||||
|                                     "Input": "Dvd" | ||||
|                                     "Input": "DVD" | ||||
|                                 } | ||||
|                             ], | ||||
|                             "VolumeDevice": "JVC AV Receiver", | ||||
| @@ -120,16 +131,19 @@ | ||||
|                             "DisplayName": "Apple TV", | ||||
|                             "DeviceSetupList": [ | ||||
|                                 { | ||||
|                                     "Hub": "Harmony", | ||||
|                                     "DeviceName": "Apple TV Gen 4", | ||||
|                                     "Input": [] | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "Hub": "Harmony", | ||||
|                                     "DeviceName": "LG TV", | ||||
|                                     "Input": "Hdmi1" | ||||
|                                     "Input": "HDMI1" | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "Hub": "Harmony", | ||||
|                                     "DeviceName": "JVC AV Receiver", | ||||
|                                     "Input": "Dvd" | ||||
|                                     "Input": "DVD" | ||||
|                                 } | ||||
|                             ], | ||||
|                             "VolumeDevice": "JVC AV Receiver", | ||||
| @@ -141,8 +155,9 @@ | ||||
|                             "DisplayName": "Listen to Music", | ||||
|                             "DeviceSetupList": [ | ||||
|                                 { | ||||
|                                     "Hub": "Harmony", | ||||
|                                     "DeviceName": "JVC AV Receiver", | ||||
|                                     "Input": "CD" | ||||
|                                     "Input": "DVD" | ||||
|                                 } | ||||
|                             ], | ||||
|                             "VolumeDevice": "JVC AV Receiver", | ||||
| @@ -159,19 +174,22 @@ | ||||
|                             "DisplayName": "Chromecast", | ||||
|                             "DeviceSetupList": [ | ||||
|                                 { | ||||
|                                     "Hub": "Neeo", | ||||
|                                     "DeviceName": "Vizio TV", | ||||
|                                     "Input": "Hdmi1" | ||||
|                                     "Input": "HDMI1" | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "DeviceName": "Harman Kardon AV Receiver", | ||||
|                                     "Hub": "Neeo", | ||||
|                                     "DeviceName": "Harmon Kardon Receicer", | ||||
|                                     "Input": "Game" | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "Hub": "Harmony", | ||||
|                                     "DeviceName": "Chromecast", | ||||
|                                     "Input": [] | ||||
|                                 } | ||||
|                             ], | ||||
|                             "VolumeDevice": "Harman Kardon AV Receiver", | ||||
|                             "VolumeDevice": "Harmon Kardon Receicer", | ||||
|                             "ControlDevice": "Chromecast", | ||||
|                             "OutputDevice": "Vizio TV", | ||||
|                             "UseMatrix": "true" | ||||
| @@ -180,19 +198,22 @@ | ||||
|                             "DisplayName": "Play Xbox One", | ||||
|                             "DeviceSetupList": [ | ||||
|                                 { | ||||
|                                     "Hub": "Harmony", | ||||
|                                     "DeviceName": "Microsoft Xbox One", | ||||
|                                     "Input": [] | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "Hub": "Neeo", | ||||
|                                     "DeviceName": "Vizio TV", | ||||
|                                     "Input": "Hdmi1" | ||||
|                                     "Input": "HDMI1" | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "DeviceName": "Harman Kardon AV Receiver", | ||||
|                                     "Hub": "Neeo", | ||||
|                                     "DeviceName": "Harmon Kardon Receicer", | ||||
|                                     "Input": "Game" | ||||
|                                 } | ||||
|                             ], | ||||
|                             "VolumeDevice": "Harman Kardon AV Receiver", | ||||
|                             "VolumeDevice": "Harmon Kardon Receicer", | ||||
|                             "ControlDevice": "Microsoft Xbox One", | ||||
|                             "OutputDevice": "Vizio TV", | ||||
|                             "UseMatrix": "true" | ||||
| @@ -201,19 +222,22 @@ | ||||
|                             "DisplayName": "Play PS4", | ||||
|                             "DeviceSetupList": [ | ||||
|                                 { | ||||
|                                     "Hub": "Harmony", | ||||
|                                     "DeviceName": "Sony PS4", | ||||
|                                     "Input": [] | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "Hub": "Neeo", | ||||
|                                     "DeviceName": "Vizio TV", | ||||
|                                     "Input": "Hdmi1" | ||||
|                                     "Input": "HDMI1" | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "DeviceName": "Harman Kardon AV Receiver", | ||||
|                                     "Input": "Dvd" | ||||
|                                     "Hub": "Neeo", | ||||
|                                     "DeviceName": "Harmon Kardon Receicer", | ||||
|                                     "Input": "DVD" | ||||
|                                 } | ||||
|                             ], | ||||
|                             "VolumeDevice": "Harman Kardon AV Receiver", | ||||
|                             "VolumeDevice": "Harmon Kardon Receicer", | ||||
|                             "ControlDevice": "Sony PS4", | ||||
|                             "OutputDevice": "LG TV", | ||||
|                             "UseMatrix": "true" | ||||
| @@ -222,19 +246,22 @@ | ||||
|                             "DisplayName": "Apple TV", | ||||
|                             "DeviceSetupList": [ | ||||
|                                 { | ||||
|                                     "Hub": "Harmony", | ||||
|                                     "DeviceName": "Apple TV Gen 4", | ||||
|                                     "Input": [] | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "Hub": "Neeo", | ||||
|                                     "DeviceName": "Vizio TV", | ||||
|                                     "Input": "Hdmi1" | ||||
|                                     "Input": "HDMI1" | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "DeviceName": "Harman Kardon AV Receiver", | ||||
|                                     "Hub": "Neeo", | ||||
|                                     "DeviceName": "Harmon Kardon Receicer", | ||||
|                                     "Input": "Game" | ||||
|                                 } | ||||
|                             ], | ||||
|                             "VolumeDevice": "Harman Kardon AV Receiver", | ||||
|                             "VolumeDevice": "Harmon Kardon Receicer", | ||||
|                             "ControlDevice": "Apple TV Gen 4", | ||||
|                             "OutputDevice": "Vizio TV", | ||||
|                             "UseMatrix": "true" | ||||
| @@ -248,12 +275,14 @@ | ||||
|                             "DisplayName": "Chromecast", | ||||
|                             "DeviceSetupList": [ | ||||
|                                 { | ||||
|                                     "Hub": "Harmony", | ||||
|                                     "DeviceName": "Chromecast", | ||||
|                                     "Input": [] | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "Hub": "Harmony", | ||||
|                                     "DeviceName": "Westinghouse TV", | ||||
|                                     "Input": "Hdmi1" | ||||
|                                     "Input": "HDMI1" | ||||
|                                 } | ||||
|                             ], | ||||
|                             "VolumeDevice": "Westinghouse TV", | ||||
| @@ -265,12 +294,14 @@ | ||||
|                             "DisplayName": "Watch Apple Tv", | ||||
|                             "DeviceSetupList": [ | ||||
|                                 { | ||||
|                                     "Hub": "Harmony", | ||||
|                                     "DeviceName": "Apple TV Gen 4", | ||||
|                                     "Input": [] | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "Hub": "Harmony", | ||||
|                                     "DeviceName": "Westinghouse TV", | ||||
|                                     "Input": "Hdmi1" | ||||
|                                     "Input": "HDMI1" | ||||
|                                 } | ||||
|                             ], | ||||
|                             "VolumeDevice": "Westinghouse TV", | ||||
| @@ -282,10 +313,12 @@ | ||||
|                             "DisplayName": "Play Xbox One", | ||||
|                             "DeviceSetupList": [ | ||||
|                                 { | ||||
|                                     "Hub": "Harmony", | ||||
|                                     "DeviceName": "Westinghouse TV", | ||||
|                                     "Input": "Hdmi1" | ||||
|                                     "Input": "HDMI1" | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "Hub": "Harmony", | ||||
|                                     "DeviceName": "Microsoft Xbox One", | ||||
|                                     "Input": [] | ||||
|                                 } | ||||
|   | ||||
							
								
								
									
										22
									
								
								deploy.sh
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								deploy.sh
									
									
									
									
									
								
							| @@ -1,22 +0,0 @@ | ||||
| #!/bin/bash | ||||
| remote_user="bmw" | ||||
| remote_server="192.168.1.31" | ||||
| deploy_location="/home/bmw/homebridge-harmony-control" | ||||
| homebridge_location="/var/lib/homebridge/" | ||||
|  | ||||
| #build | ||||
| tsc --build | ||||
| #copy files to remote machine | ||||
| scp -r bin $remote_user@$remote_server:$deploy_location | ||||
| scp package.json $remote_user@$remote_server:$deploy_location | ||||
|  | ||||
| #install package | ||||
| ssh -t $remote_user@$remote_server "sudo npm install -g --unsafe-perm $deploy_location" | ||||
|  | ||||
| #restart service | ||||
| ssh -t | ||||
| ssh -t $remote_user@$remote_server "sudo systemctl restart homebridge.service" | ||||
| ssh -t $remote_user@$remote_server "sudo systemctl status homebridge.service" | ||||
|  | ||||
| echo done | ||||
| exit | ||||
							
								
								
									
										13
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										13
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -748,6 +748,14 @@ | ||||
|       "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.7.tgz", | ||||
|       "integrity": "sha512-1YKeT4JitGgE4SOzyB9eMwO0nGVNkNEsm9qlIt1Lqm/tG2QEiSMTD4kS3aO6L+w5SClLVxALmIBESK6Mk5wX0A==" | ||||
|     }, | ||||
|     "@types/node-fetch": { | ||||
|       "version": "2.5.0", | ||||
|       "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.0.tgz", | ||||
|       "integrity": "sha512-TLFRywthBgL68auWj+ziWu+vnmmcHCDFC/sqCOQf1xTz4hRq8cu79z8CtHU9lncExGBsB8fXA4TiLDLt6xvMzw==", | ||||
|       "requires": { | ||||
|         "@types/node": "*" | ||||
|       } | ||||
|     }, | ||||
|     "ajv": { | ||||
|       "version": "6.10.0", | ||||
|       "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", | ||||
| @@ -1541,6 +1549,11 @@ | ||||
|       "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", | ||||
|       "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" | ||||
|     }, | ||||
|     "node-fetch": { | ||||
|       "version": "2.6.0", | ||||
|       "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", | ||||
|       "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" | ||||
|     }, | ||||
|     "node-persist": { | ||||
|       "version": "0.0.8", | ||||
|       "resolved": "https://registry.npmjs.org/node-persist/-/node-persist-0.0.8.tgz", | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "homebridge-harmony-control", | ||||
|     "version": "1.0.7", | ||||
|     "version": "1.0.0", | ||||
|     "description": "Homebridge platform to control smart home equipment by room.", | ||||
|     "main": "bin/index.js", | ||||
|     "scripts": { | ||||
| @@ -35,8 +35,10 @@ | ||||
|         "@babel/preset-env": "^7.4.5", | ||||
|         "@babel/preset-typescript": "^7.3.3", | ||||
|         "@types/node": "^12.0.7", | ||||
|         "@types/node-fetch": "^2.5.0", | ||||
|         "harmony-websocket": "^1.1.0", | ||||
|         "homebridge": "^0.4.50", | ||||
|         "node-fetch": "^2.6.0", | ||||
|         "request": "^2.88.0" | ||||
|     } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -1,7 +1,8 @@ | ||||
| import * as Config from "../Models/Config" | ||||
| import { Activity } from '../Models/Activity'; | ||||
| import { Matrix } from '../Models/Matrix'; | ||||
| import { IAccessory } from './IAccessory'; | ||||
| import callbackify from '../Util/Callbackify'; | ||||
| import HarmonyDataProvider from '../DataProviders/HarmonyDataProvider'; | ||||
| import HubDataProvider from '../DataProviders/HubDataProvider'; | ||||
|  | ||||
| let Service: HAPNodeJS.Service; | ||||
| let Characteristic: HAPNodeJS.Characteristic; | ||||
| @@ -28,33 +29,38 @@ export enum RemoteKey { | ||||
| } | ||||
|  | ||||
| export interface IControlUnitProps { | ||||
|     dataProvider: HarmonyDataProvider, | ||||
|     dataProvider: HubDataProvider, | ||||
|     displayName: string, | ||||
|     activities: Array<Config.IActivity>, | ||||
|     activities: Array<Activity>, | ||||
|     api: any, | ||||
|     log: any, | ||||
|     homebridge: any, | ||||
|     isExternal: boolean, | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * ControlUnit accessory | ||||
|  */ | ||||
| export class ControlUnit implements IAccessory { | ||||
|  | ||||
|     //Required by homebridge | ||||
|     name: string = ""; | ||||
|  | ||||
|     //fields | ||||
|     private log: any = {}; | ||||
|     private displayName: string = ""; | ||||
|     private isExternal: boolean = false; | ||||
|  | ||||
|     //Service fields | ||||
|     private _televisionService: HAPNodeJS.Service | undefined; | ||||
|     private _televisionSpeakerService: HAPNodeJS.Service | undefined; | ||||
|     private _infoService: HAPNodeJS.Service; | ||||
|     private _inputServices: Array<HAPNodeJS.Service | undefined> = []; | ||||
|     private televisionService: HAPNodeJS.Service | undefined; | ||||
|     private televisionSpeakerService: HAPNodeJS.Service | undefined; | ||||
|     private informationService: HAPNodeJS.Service | undefined; | ||||
|     private inputServices: Array<HAPNodeJS.Service | undefined> = []; | ||||
|  | ||||
|     //Harmony fields | ||||
|     private _activities: Array<Config.IActivity> = []; | ||||
|     private _dataProvider: HarmonyDataProvider; | ||||
|     private activities: Array<Activity> = []; | ||||
|     private dataProvider: HubDataProvider; | ||||
|  | ||||
|     public platformAccessory: any; | ||||
|  | ||||
|     /** | ||||
|      * Constructor | ||||
| @@ -67,21 +73,16 @@ export class ControlUnit implements IAccessory { | ||||
|         Service = props.api.hap.Service; | ||||
|         Characteristic = props.api.hap.Characteristic; | ||||
|         this.name = props.displayName; | ||||
|         this.displayName = props.displayName; | ||||
|         this.displayName = props.isExternal ? `${props.displayName}-Remote` : props.displayName; | ||||
|         this.isExternal = props.isExternal; | ||||
|  | ||||
|         this._activities = props.activities; | ||||
|         this.activities = props.activities; | ||||
|  | ||||
|         this._dataProvider = props.dataProvider; | ||||
|         this.dataProvider = props.dataProvider; | ||||
|         homebridge = props.homebridge; | ||||
|  | ||||
|         this.platformAccessory = new homebridge.platformAccessory(this.displayName, this.generateUUID(), homebridge.hap.Accessory.Categories.TELEVISION); | ||||
|  | ||||
|         //@ts-ignore | ||||
|         this._infoService = new Service.AccessoryInformation(); | ||||
|         this._infoService.setCharacteristic(Characteristic.Manufacturer, "Brandon Watson") | ||||
|         this._infoService.setCharacteristic(Characteristic.Model, "Matrix Output Television") | ||||
|         this._infoService.setCharacteristic(Characteristic.SerialNumber, "123-456-789"); | ||||
|  | ||||
|         //Configure services | ||||
|         this.configureTvService(); | ||||
|  | ||||
| @@ -100,15 +101,10 @@ export class ControlUnit implements IAccessory { | ||||
|             //@ts-ignore | ||||
|             if (service.linked) { | ||||
|                 //@ts-ignore | ||||
|                 this._televisionService!.addLinkedService(service); | ||||
|                 this.televisionService!.addLinkedService(service); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     //Required by homebridge | ||||
|     name: string = ""; | ||||
|     public platformAccessory: any; | ||||
|  | ||||
|     /************* | ||||
|      *  | ||||
|      * Tv Service | ||||
| @@ -119,31 +115,32 @@ export class ControlUnit implements IAccessory { | ||||
|      * Configure television service | ||||
|      */ | ||||
|     private configureTvService(): void { | ||||
|         this._televisionService = new Service.Television( | ||||
|         this.televisionService = new Service.Television( | ||||
|             this.displayName, | ||||
|             'tvService' | ||||
|         ) | ||||
|  | ||||
|         this._televisionService.setCharacteristic(Characteristic.ConfiguredName, this.displayName); | ||||
|         this.televisionService.setCharacteristic(Characteristic.ConfiguredName, this.displayName); | ||||
|         //@ts-ignore | ||||
|         this._televisionService.setCharacteristic(Characteristic.SleepDiscoveryMode, Characteristic.SleepDiscoveryMode.ALWAYS_DISCOVERABLE); | ||||
|         this._televisionService.setCharacteristic(Characteristic.ActiveIdentifier, 1); | ||||
|         this._televisionService.setCharacteristic(Characteristic.Active, false); | ||||
|         this.televisionService.setCharacteristic(Characteristic.SleepDiscoveryMode, Characteristic.SleepDiscoveryMode.ALWAYS_DISCOVERABLE); | ||||
|         this.televisionService.setCharacteristic(Characteristic.ActiveIdentifier, 1); | ||||
|         this.televisionService.setCharacteristic(Characteristic.Active, false); | ||||
|  | ||||
|         //setup listeners | ||||
|         this._televisionService.getCharacteristic(Characteristic.Active) | ||||
|         this.televisionService.getCharacteristic(Characteristic.Active) | ||||
|             //@ts-ignore | ||||
|             .on("set", callbackify(this.onSetAccessoryActive)) | ||||
|             //@ts-ignore | ||||
|             .on("get", callbackify(this.onGetAccessoryActive)); | ||||
|  | ||||
|         //Set remote characteristics if is external | ||||
|         this._televisionService.getCharacteristic(Characteristic.RemoteKey) | ||||
|             //@ts-ignore | ||||
|             .on("set", callbackify(this.onSetRemoteKey)); | ||||
|         if (this.isExternal) { | ||||
|             this.televisionService.getCharacteristic(Characteristic.RemoteKey) | ||||
|                 //@ts-ignore | ||||
|                 .on("set", callbackify(this.onSetRemoteKey)); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         this._televisionService.getCharacteristic(Characteristic.ActiveIdentifier) | ||||
|         this.televisionService.getCharacteristic(Characteristic.ActiveIdentifier) | ||||
|             //@ts-ignore | ||||
|             .on("set", callbackify(this.onSetActiveIdentifier)) | ||||
|             //@ts-ignore | ||||
| @@ -154,10 +151,12 @@ export class ControlUnit implements IAccessory { | ||||
|      * Event handler for SET active characteristic | ||||
|      */ | ||||
|     private onSetAccessoryActive = async (value: any) => { | ||||
|         switch (value) { | ||||
|             case 0: this._dataProvider.powerOff(this.name); break; | ||||
|             //Turn on with first activity | ||||
|             case 1: this._dataProvider.powerOn(this.name, this._activities[0]); break; | ||||
|         if (!this.isExternal) { | ||||
|             switch (value) { | ||||
|                 case 0: this.dataProvider.powerOff(this.name); break; | ||||
|                 //Turn on with first activity | ||||
|                 case 1: this.dataProvider.powerOn(this.name, this.activities[0]); break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -166,31 +165,35 @@ export class ControlUnit implements IAccessory { | ||||
|      */ | ||||
|     private onGetAccessoryActive = async () => { | ||||
|         //@ts-ignore | ||||
|         return this._dataProvider.getIsActive(this.name) ? Characteristic.Active.Active : Characteristic.Active.Inactive | ||||
|         return this.dataProvider.getIsActive() ? Characteristic.Active.Active : Characteristic.Active.Inactive | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Event handler for SET remote key | ||||
|      */ | ||||
|     private onSetRemoteKey = async (key: any) => { | ||||
|         this._dataProvider.sendKeyPress(this.name, key); | ||||
|         if (this.isExternal) { | ||||
|             this.dataProvider.sendKeyPress(this.name, key); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Event handler for SET active identifier characteristic | ||||
|      */ | ||||
|     private onSetActiveIdentifier = async (identifier: any) => { | ||||
|         this._dataProvider.startActivity(this.name, this._activities[identifier]); | ||||
|         if (!this.isExternal) { | ||||
|             this.dataProvider.startActivity(this.name, this.activities[identifier]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Event handler for GET active identifier characteristic | ||||
|      */ | ||||
|     private onGetActiveIdentifier = async () => { | ||||
|         let currentActivity: Config.IActivity = this._dataProvider.getIsActive(this.name)!; | ||||
|         let currentActivity: Activity = this.dataProvider.getIsActive(this.name)!; | ||||
|         let identifier: number = 0; | ||||
|         if (currentActivity) { | ||||
|             identifier = this._activities.findIndex(e => e.DisplayName === currentActivity.DisplayName); | ||||
|             identifier = this.activities.findIndex(e => e.displayName === currentActivity.displayName); | ||||
|         } | ||||
|         return identifier; | ||||
|     } | ||||
| @@ -205,23 +208,23 @@ export class ControlUnit implements IAccessory { | ||||
|      * Configure Speaker Service | ||||
|      */ | ||||
|     private configureTvSpeakerService(): void { | ||||
|         this._televisionSpeakerService = new Service.TelevisionSpeaker( | ||||
|         this.televisionSpeakerService = new Service.TelevisionSpeaker( | ||||
|             this.displayName, | ||||
|             'tvSpeakerService' | ||||
|         ); | ||||
|         this._televisionSpeakerService.setCharacteristic(Characteristic.Name, this.displayName); | ||||
|         this.televisionSpeakerService.setCharacteristic(Characteristic.Name, this.displayName); | ||||
|         //@ts-ignore | ||||
|         this._televisionSpeakerService.setCharacteristic(Characteristic.Active, Characteristic.Active.ACTIVE); | ||||
|         this.televisionSpeakerService.setCharacteristic(Characteristic.Active, Characteristic.Active.ACTIVE); | ||||
|         //@ts-ignore | ||||
|         this._televisionSpeakerService.setCharacteristic(Characteristic.VolumeControlType, Characteristic.VolumeControlType.ABSOLUTE); | ||||
|         this._televisionSpeakerService.subtype = this.displayName + "Volume"; | ||||
|         if (this._televisionService) { | ||||
|         this.televisionSpeakerService.setCharacteristic(Characteristic.VolumeControlType, Characteristic.VolumeControlType.ABSOLUTE); | ||||
|         this.televisionSpeakerService.subtype = this.displayName + "Volume"; | ||||
|         if (this.televisionService) { | ||||
|             //@ts-ignore | ||||
|             this._televisionService.addLinkedService(this._televisionSpeakerService); | ||||
|             this.televisionService.addLinkedService(this.televisionSpeakerService); | ||||
|         } | ||||
|  | ||||
|         //Setup listeners | ||||
|         this._televisionSpeakerService.getCharacteristic(Characteristic.VolumeSelector) | ||||
|         this.televisionSpeakerService.getCharacteristic(Characteristic.VolumeSelector) | ||||
|             //@ts-ignore | ||||
|             .on("set", callbackify(this.onSetVolumeSelector)); | ||||
|     } | ||||
| @@ -230,12 +233,30 @@ export class ControlUnit implements IAccessory { | ||||
|      * Event handler for SET volume characteristic | ||||
|      */ | ||||
|     private onSetVolumeSelector = async (value: any) => { | ||||
|         switch (value) { | ||||
|             case 0: this._dataProvider.volumeUp(this.name); break; | ||||
|             case 1: this._dataProvider.volumeDown(this.name); break; | ||||
|         if (this.isExternal) { | ||||
|             switch (value) { | ||||
|                 case 0: this.dataProvider.volumeUp(this.name); break; | ||||
|                 case 1: this.dataProvider.volumeDown(this.name); break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /********************* | ||||
|      *  | ||||
|      * Information Service | ||||
|      *  | ||||
|      ********************/ | ||||
|  | ||||
|     /** | ||||
|      * Configure information service | ||||
|      */ | ||||
|     private configureAccessoryInformation(): void { | ||||
|         this.informationService = new Service.AccessoryInformation(this.displayName, 'information'); | ||||
|         this.informationService | ||||
|             .setCharacteristic(Characteristic.Manufacturer, 'Loftux Carwings') | ||||
|             .setCharacteristic(Characteristic.Model, 'Heater-Cooler') | ||||
|     } | ||||
|  | ||||
|     /***************** | ||||
|      *  | ||||
|      * Input services | ||||
| @@ -247,13 +268,13 @@ export class ControlUnit implements IAccessory { | ||||
|      */ | ||||
|     private configureInputSourceService(): void { | ||||
|         let inputs: Array<HAPNodeJS.Service> = []; | ||||
|         this._activities.forEach((activity: Config.IActivity, index: number) => { | ||||
|             let inputService = new Service.InputSource(activity.DisplayName, 'activity' + activity.DisplayName); | ||||
|         this.activities.forEach((activity: Activity, index: number) => { | ||||
|             let inputService = new Service.InputSource(activity.displayName, 'activity' + activity.displayName); | ||||
|             inputService | ||||
|                 .setCharacteristic(Characteristic.Identifier, index) | ||||
|                 .setCharacteristic( | ||||
|                     Characteristic.ConfiguredName, | ||||
|                     activity.DisplayName) | ||||
|                     activity.displayName) | ||||
|                 .setCharacteristic( | ||||
|                     Characteristic.IsConfigured, | ||||
|                     //@ts-ignore | ||||
| @@ -263,10 +284,10 @@ export class ControlUnit implements IAccessory { | ||||
|                 .setCharacteristic(Characteristic.InputSourceType, Characteristic.InputSourceType.HDMI); | ||||
|  | ||||
|             //@ts-ignore | ||||
|             this._televisionService!.addLinkedService(inputService); | ||||
|             this.televisionService!.addLinkedService(inputService); | ||||
|             inputs.push(inputService); | ||||
|         }); | ||||
|         this._inputServices = inputs; | ||||
|         this.inputServices = inputs; | ||||
|     } | ||||
|  | ||||
|     private generateUUID(): string { // Public Domain/MIT | ||||
| @@ -285,8 +306,8 @@ export class ControlUnit implements IAccessory { | ||||
|      * Called by homebridge to gather services for this accessory. | ||||
|      */ | ||||
|     getServices(): Array<HAPNodeJS.Service> { | ||||
|         let services: Array<HAPNodeJS.Service> = [this._infoService, this._televisionService!, this._televisionSpeakerService!]; | ||||
|         this._inputServices.forEach((service: HAPNodeJS.Service | undefined) => { | ||||
|         let services: Array<HAPNodeJS.Service> = [this.televisionService!, this.televisionSpeakerService!]; | ||||
|         this.inputServices.forEach((service: HAPNodeJS.Service | undefined) => { | ||||
|             services.push(service!); | ||||
|         }); | ||||
|         return (services); | ||||
|   | ||||
| @@ -1,155 +0,0 @@ | ||||
| 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; | ||||
|  | ||||
| export interface IDeviceButtonProps { | ||||
|     dataProvider: HarmonyDataProvider, | ||||
|     buttonName: string, | ||||
|     displayName: string, | ||||
|     deviceInfo: IDeviceButton, | ||||
|     api: any, | ||||
|     log: any, | ||||
|     homebridge: any, | ||||
| } | ||||
|  | ||||
| export class DeviceButton implements IAccessory { | ||||
|     private _api: any; | ||||
|     private _homebridge: any; | ||||
|     private _log: any = {}; | ||||
|  | ||||
|     //Service fields | ||||
|     private _switchService: HAPNodeJS.Service; | ||||
|     private _infoService: HAPNodeJS.Service; | ||||
|  | ||||
|     private _buttonInfo: IDeviceButton; | ||||
|  | ||||
|     private _dataProvider: HarmonyDataProvider; | ||||
|  | ||||
|     private _device!: HarmonyDevice; | ||||
|  | ||||
|     private _buttonState: boolean; | ||||
|  | ||||
|     constructor(props: IDeviceButtonProps) { | ||||
|         //Assign class variables | ||||
|         this._log = props.log; | ||||
|         this._api = props.api; | ||||
|         Service = props.api.hap.Service; | ||||
|         Characteristic = props.api.hap.Characteristic; | ||||
|         this.name = props.displayName; | ||||
|         this._homebridge = props.homebridge; | ||||
|  | ||||
|         this._buttonInfo = props.deviceInfo; | ||||
|  | ||||
|         this._dataProvider = props.dataProvider; | ||||
|  | ||||
|         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 | ||||
|         this._infoService = new Service.AccessoryInformation(); | ||||
|         this._infoService.setCharacteristic(Characteristic.Manufacturer, "Brandon Watson") | ||||
|         this._infoService.setCharacteristic(Characteristic.Model, "Device Button") | ||||
|         this._infoService.setCharacteristic(Characteristic.SerialNumber, "123-456-789"); | ||||
|  | ||||
|         this._switchService = new Service.Switch( | ||||
|             this.name, | ||||
|             'switchService' | ||||
|         ) | ||||
|  | ||||
|         this._switchService.getCharacteristic(Characteristic.On) | ||||
|             //@ts-ignore | ||||
|             .on("set", this.onSwitchSet) | ||||
|             .updateValue(this._buttonState) | ||||
|             .on("get", this.onSwitchGet); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Required by homebridge. | ||||
|      */ | ||||
|     public name: string; | ||||
|  | ||||
|     public platformAccessory: any; | ||||
|  | ||||
|     /** | ||||
|      * Called by homebridge to gather services. | ||||
|      */ | ||||
|     public getServices = (): Array<HAPNodeJS.Service> => { | ||||
|         return [this._infoService, this._switchService!]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handler for switch set event | ||||
|      * @param callback The callback function to call when complete | ||||
|      */ | ||||
|     private onSwitchSet = async (newState: boolean, callback: (error?: Error | null | undefined) => void) => { | ||||
|         if (!this._buttonInfo.IsStateful && newState === this._buttonState) { | ||||
|             return callback(); | ||||
|         } | ||||
|  | ||||
|         //Get device command if we don't have it | ||||
|         if (!this._device) { | ||||
|             this._device = this._dataProvider.getDeviceFromName(this._buttonInfo.DeviceName); | ||||
|         } | ||||
|  | ||||
|         //Execute command | ||||
|         if (this._device) { | ||||
|             //change state if stateful | ||||
|             if (this._buttonInfo.IsStateful && this._buttonState != newState) { | ||||
|                 this._buttonState = newState | ||||
|                 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++) { | ||||
|                     await this._device.sendCommand(this._buttonInfo.ButtonName); | ||||
|                 } | ||||
|                 this._switchService.getCharacteristic(Characteristic.On).updateValue(false); | ||||
|                 return callback(new Error("Normal Response")); | ||||
|             } | ||||
|         } | ||||
|         return callback(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handler for switch get event | ||||
|      * @param callback The callback function to call when complete | ||||
|      */ | ||||
|     private onSwitchGet = (callback: (error: Error | null, value: boolean) => void) => { | ||||
|         //Only return state if button is stateful | ||||
|         if (this._buttonInfo.IsStateful) { | ||||
|             return callback(null, this._buttonState); | ||||
|         } else { | ||||
|             return callback(null, false) | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Helper function to generate a UUID | ||||
|      */ | ||||
|     private generateUUID(): string { // Public Domain/MIT | ||||
|         var d = new Date().getTime(); | ||||
|         if (typeof performance !== 'undefined' && typeof performance.now === 'function') { | ||||
|             d += performance.now(); //use high-precision timer if available | ||||
|         } | ||||
|         return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { | ||||
|             var r = (d + Math.random() * 16) % 16 | 0; | ||||
|             d = Math.floor(d / 16); | ||||
|             return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| @@ -1,3 +0,0 @@ | ||||
| export { ControlUnit } from './ControlUnit'; | ||||
| export { DeviceButton } from './DeviceButton'; | ||||
| export { IAccessory } from './IAccessory'; | ||||
| @@ -1,348 +0,0 @@ | ||||
| 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 { EventEmitter } from "events"; | ||||
| 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; | ||||
|  | ||||
| const Harmony = require("harmony-websocket"); | ||||
|  | ||||
| interface IActivityState { | ||||
|     currentActivity: IActivity | ||||
| } | ||||
|  | ||||
| interface IHarmonyDataProviderProps { | ||||
|     hubs: Array<IHub>; | ||||
|     deviceConfigs: Array<IDeviceConfig>; | ||||
|     log: any, | ||||
|     matrix: IMatrix | ||||
| } | ||||
|  | ||||
| class HarmonyDataProvider extends EventEmitter { | ||||
|     private _log: any; | ||||
|     private _hubsByDevice: { [deviceName: string]: string } = {}; | ||||
|     private _hubs: { [hubName: string]: HarmonyHub } = {}; | ||||
|     // private _devicesByHub: { [hubName: string]: { [deviceName: string]: HarmonyDevice } } = {}; | ||||
|     private _states: { [controlUnitName: string]: (IActivityState | undefined) } = {}; | ||||
|  | ||||
|     private _matrix: IMatrix; | ||||
|  | ||||
|     constructor(props: IHarmonyDataProviderProps) { | ||||
|         super(); | ||||
|         this._log = props.log; | ||||
|         this._matrix = props.matrix; | ||||
|         props.deviceConfigs.forEach((deviceConfig: IDeviceConfig) => { | ||||
|             this._hubsByDevice[deviceConfig.Name] = deviceConfig.Hub; | ||||
|         }); | ||||
|         // this._deviceConfigs = props.deviceConfigs; | ||||
|  | ||||
|         this.connect(props.hubs); | ||||
|     } | ||||
|  | ||||
|     // 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<HarmonyDevice> = 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(`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<HarmonyDevice> = 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 | ||||
|         await Promise.all(devicesToTurnOn.map(async (device: HarmonyDevice) => { | ||||
|             if (device && device.name) { | ||||
|                 if (!device.on) { | ||||
|                     this._log(`Turning on device ${device.name}`) | ||||
|                     await device.powerOn(); | ||||
|                 } | ||||
|             } | ||||
|         })); | ||||
|  | ||||
|         //Assign correct input | ||||
|         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}`) | ||||
|                 } | ||||
|             }) | ||||
|         ); | ||||
|  | ||||
|         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<HarmonyDevice> = 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(`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(`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; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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(`Error retrieving device from hub: ${err}`); | ||||
|         } | ||||
|  | ||||
|         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<IHub>) => { | ||||
|         let readyCount = 0; | ||||
|         await Promise.all( | ||||
|             hubs.map(async (hub: IHub): Promise<void> => { | ||||
|                 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(); | ||||
|             }) | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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<HarmonyDevice>, controlUnitName: string): Array<HarmonyDevice> { | ||||
|         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; | ||||
|     } | ||||
| } | ||||
|  | ||||
| export default HarmonyDataProvider; | ||||
							
								
								
									
										508
									
								
								src/DataProviders/HubDataProvider.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										508
									
								
								src/DataProviders/HubDataProvider.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,508 @@ | ||||
| import { Activity } from "../Models/Activity"; | ||||
| import { DeviceSetupItem } from "../Models/DeviceSetupItem"; | ||||
| import { Input, Matrix, Output } from "../Models/Matrix"; | ||||
| import { IDevice } from './IDevice'; | ||||
| import { RemoteKey } from '../Accessories/ControlUnit'; | ||||
| import { sleep } from '../Util/Sleep'; | ||||
| import { hub } from ".."; | ||||
| import fetch from 'node-fetch'; | ||||
|  | ||||
| let Characteristic: HAPNodeJS.Characteristic; | ||||
|  | ||||
| const Harmony = require("harmony-websocket"); | ||||
|  | ||||
| interface IActivityState { | ||||
|     currentActivity: Activity | ||||
| } | ||||
|  | ||||
| interface IHubDataProviderProps { | ||||
|     harmonyHubAddress: string, | ||||
|     neeoHubAddress: string, | ||||
|     log: any, | ||||
|     matrix: Matrix | ||||
| } | ||||
|  | ||||
| interface INeeoMacro { | ||||
|     roomKey: string, | ||||
|     macroKey: string, | ||||
|     deviceKey: string, | ||||
| } | ||||
|  | ||||
| class HubDataProvider { | ||||
|     //Harmony Fields | ||||
|     private harmonyHubAddress: string; | ||||
|     private harmonyConnected: boolean = false; | ||||
|  | ||||
|     //Neeo Fields | ||||
|     private neeoHubAddress: string; | ||||
|  | ||||
|     //Common Fields | ||||
|     private log: any; | ||||
|  | ||||
|     private devices: { [name: string]: IDevice; } = {}; | ||||
|     private states: { [controlUnitName: string]: (IActivityState | undefined) } = {}; | ||||
|  | ||||
|     private matrix: Matrix; | ||||
|  | ||||
|     constructor(props: IHubDataProviderProps) { | ||||
|         this.log = props.log; | ||||
|         this.harmonyHubAddress = props.harmonyHubAddress; | ||||
|         this.neeoHubAddress = props.neeoHubAddress; | ||||
|         this.matrix = props.matrix; | ||||
|  | ||||
|         if (this.harmonyHubAddress) { | ||||
|             try { | ||||
|                 Promise.resolve(this.connectHarmony(this.harmonyHubAddress)); | ||||
|             } catch (err) { | ||||
|                 this.log(`Error connecting to harmony hub at ${this.harmonyHubAddress}: ${err}`); | ||||
|             } | ||||
|         } | ||||
|         if (this.neeoHubAddress) { | ||||
|             try { | ||||
|                 Promise.resolve(this.connectNeeo(this.neeoHubAddress)); | ||||
|             } catch (err) { | ||||
|                 this.log(`Error connecting to neeo hub at ${this.neeoHubAddress}: ${err}`) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Power on all devices in an activity. | ||||
|      */ | ||||
|     public powerOn = async (controlUnitName: string, activity: Activity) => { | ||||
|         //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<IDevice> = this.states[controlUnitName]!.currentActivity.deviceSetupItems | ||||
|             .map((value: DeviceSetupItem): IDevice => { | ||||
|                 return this.getDeviceFromName(value.deviceName, value.hubType); | ||||
|             }); | ||||
|  | ||||
|         //Resolve device conflicts with other controlUnits | ||||
|         devicesToTurnOff = this.sanitizeDeviceList(devicesToTurnOff, controlUnitName); | ||||
|  | ||||
|         await Promise.all( | ||||
|             //Turn off devices | ||||
|             devicesToTurnOff.map(async (device: IDevice) => { | ||||
|                 if (device) { | ||||
|                     await device.powerOff(); | ||||
|                 } | ||||
|             }) | ||||
|         ); | ||||
|  | ||||
|  | ||||
|         this.states[controlUnitName] = undefined; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Start an activity | ||||
|      */ | ||||
|     public startActivity = async (controlUnitName: string, activity: Activity) => { | ||||
|         this.log(`Starting activity ${activity.displayName} for controlUnit: ${controlUnitName}`) | ||||
|         let lastActivity: Activity | undefined = undefined; | ||||
|         if (this.states[controlUnitName]) { | ||||
|             lastActivity = this.states[controlUnitName]!.currentActivity; | ||||
|         } | ||||
|  | ||||
|         //Build potential list of devices to to turn on | ||||
|         let devicesToTurnOn: Array<IDevice> = activity.deviceSetupItems.map((value: DeviceSetupItem): IDevice => { | ||||
|             return this.getDeviceFromName(value.deviceName, value.hubType); | ||||
|         }); | ||||
|  | ||||
|         //Resolve device conflicts with other controlUnits | ||||
|         devicesToTurnOn = this.sanitizeDeviceList(devicesToTurnOn, controlUnitName); | ||||
|  | ||||
|         //Turn on devices | ||||
|         await Promise.all(devicesToTurnOn.map(async (device: IDevice) => { | ||||
|             if (device && device.name) { | ||||
|                 if (!device.on) { | ||||
|                     this.log(`Turning on device ${device.name}`) | ||||
|                     await device.powerOn(); | ||||
|                 } | ||||
|             } | ||||
|         })); | ||||
|  | ||||
|         //Assign correct input | ||||
|         await Promise.all( | ||||
|             activity.deviceSetupItems.map(async (value: DeviceSetupItem) => { | ||||
|                 let device: IDevice = this.getDeviceFromName(value.deviceName, value.hubType); | ||||
|  | ||||
|                 if (device && device.supportsCommand(`Input ${value.input}`)) { | ||||
|                     let command: string = device.getCommand(`Input ${value.input}`); | ||||
|                     await device.sendCommand(command); | ||||
|                 } | ||||
|             }) | ||||
|         ); | ||||
|  | ||||
|         if (activity.useMatrix) { | ||||
|             //get input and output | ||||
|             let input: Input = this.matrix.inputs.filter(e => e.inputDevice === activity.controlDeviceId)[0]; | ||||
|             let output: Output = this.matrix.outputs.filter(e => e.outputDevice === activity.outputDeviceId)[0]; | ||||
|  | ||||
|             let inputCommandName: string = `In ${input.inputNumber}`; | ||||
|             let outputCommandName: string = `Out ${output.outputLetter}`; | ||||
|  | ||||
|             let matrixDevice: IDevice = this.getDeviceFromName(this.matrix.deviceName, this.matrix.hubType); | ||||
|  | ||||
|             //Route hdmi | ||||
|             if (matrixDevice.supportsCommand(inputCommandName) && matrixDevice.supportsCommand(outputCommandName)) { | ||||
|                 await matrixDevice.sendCommand(matrixDevice.getCommand(inputCommandName)); | ||||
|                 await matrixDevice.sendCommand(matrixDevice.getCommand(outputCommandName)); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         //Build potential list of devices to turn off | ||||
|         if (lastActivity) { | ||||
|             let devicesToTurnOff: Array<IDevice> = lastActivity.deviceSetupItems.map((value: DeviceSetupItem): IDevice => { | ||||
|                 return this.getDeviceFromName(value.deviceName, value.hubType); | ||||
|             }); | ||||
|  | ||||
|             //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) => { | ||||
|                 if (device && device.name && activity.deviceSetupItems.some(e => { | ||||
|                     return (e && e.deviceName === device.name) | ||||
|                 })) { | ||||
|                     delete devicesToTurnOff[index]; | ||||
|                 } | ||||
|             }) | ||||
|  | ||||
|             //Resolve device conflicts with other controlUnits | ||||
|             devicesToTurnOff = this.sanitizeDeviceList(devicesToTurnOff, controlUnitName); | ||||
|  | ||||
|             this.log(`Sanatized devices to turn off: ${JSON.stringify(devicesToTurnOff.map(e => e ? e.name : ""))}`); | ||||
|  | ||||
|             await Promise.all( | ||||
|                 //Turn off devices | ||||
|                 devicesToTurnOff.map(async (device: IDevice) => { | ||||
|                     if (device) { | ||||
|                         if (device.on) { | ||||
|                             this.log(`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" | ||||
|         let currentActivity: Activity = this.states[controlUnitName]!.currentActivity; | ||||
|         let deviceSetupItem: DeviceSetupItem = currentActivity.deviceSetupItems.filter(e => e.deviceName == currentActivity.volumeDeviceId)[0]; | ||||
|  | ||||
|         if (this.states[controlUnitName]) { | ||||
|             let volumeDevice: IDevice = this.getDeviceFromName(currentActivity.volumeDeviceId, deviceSetupItem.hubType); | ||||
|             if (volumeDevice.supportsCommand(volumeUpCommand)) { | ||||
|                 volumeDevice.sendCommand(volumeDevice.getCommand(volumeUpCommand)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Volume down for current running activity. | ||||
|      */ | ||||
|     public volumeDown = async (controlUnitName: string) => { | ||||
|         let volumeDownCommand: string = "Volume Down" | ||||
|         let currentActivity: Activity = this.states[controlUnitName]!.currentActivity; | ||||
|         let deviceSetupItem: DeviceSetupItem = currentActivity.deviceSetupItems.filter(e => e.deviceName == currentActivity.volumeDeviceId)[0]; | ||||
|  | ||||
|         if (this.states[controlUnitName]) { | ||||
|             let volumeDevice: IDevice = this.getDeviceFromName(currentActivity.volumeDeviceId, deviceSetupItem.hubType); | ||||
|             if (volumeDevice.supportsCommand(volumeDownCommand)) { | ||||
|                 volumeDevice.sendCommand(volumeDevice.getCommand(volumeDownCommand)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return if a control unit is active | ||||
|      * @param controlUnitName  | ||||
|      */ | ||||
|     public getIsActive(controlUnitName: string): Activity | undefined { | ||||
|         return this.states[controlUnitName] ? this.states[controlUnitName]!.currentActivity : undefined; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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<IDevice>, controlUnitName: string): Array<IDevice> { | ||||
|         for (let controlUnitKey in this.states) { | ||||
|             //Skip self | ||||
|             if (controlUnitKey === controlUnitName) { | ||||
|                 continue; | ||||
|             } | ||||
|             let currentOtherState: IActivityState = this.states[controlUnitKey]!; | ||||
|  | ||||
|             if (currentOtherState) { | ||||
|                 currentOtherState.currentActivity.deviceSetupItems.forEach((value: DeviceSetupItem) => { | ||||
|                     //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]; | ||||
|                         delete devicesToTurnOn[devicesToTurnOn.indexOf(deviceToRemove)]; | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return devicesToTurnOn; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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 currentActivity: Activity = this.states[controlUnitName]!.currentActivity; | ||||
|             let deviceSetupItem: DeviceSetupItem = currentActivity.deviceSetupItems.filter(e => e.deviceName == currentActivity.volumeDeviceId)[0]; | ||||
|  | ||||
|             let device: IDevice = this.getDeviceFromName(this.states[controlUnitName]!.currentActivity.controlDeviceId, deviceSetupItem.hubType); | ||||
|             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; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (device && device.supportsCommand(commandName)) { | ||||
|                 device.sendCommand(device.getCommand(commandName)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the IDevice by name. | ||||
|      * @param deviceName The device to retrieve. | ||||
|      */ | ||||
|     private getDeviceFromName(deviceName: string, hubType: hub): IDevice { | ||||
|         return this.devices[`${hubType}-${deviceName}`]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Connect to harmony hub and receive device info. | ||||
|      */ | ||||
|     private connectHarmony = async (address: string): Promise<void> => { | ||||
|         let harmony: any = new Harmony(); | ||||
|         //Listeners | ||||
|         harmony.on('open', () => { | ||||
|             this.harmonyConnected = true; | ||||
|         }); | ||||
|         harmony.on('close', () => { | ||||
|             this.harmonyConnected = false; | ||||
|         }); | ||||
|         await harmony.connect(address); | ||||
|         let self = this; | ||||
|  | ||||
|         setTimeout(async function () { | ||||
|             if (self.harmonyConnected) { | ||||
|                 let devices: any = await harmony.getDevices(); | ||||
|                 try { | ||||
|                     await Promise.all( | ||||
|                         //Add each to dictionary | ||||
|                         devices.map(async (dev: any) => { | ||||
|                             //get commands | ||||
|                             let commands: { [name: string]: string } = {}; | ||||
|                             let deviceCommands: any = await harmony.getDeviceCommands(dev.id); | ||||
|                             deviceCommands.forEach((command: any) => { | ||||
|                                 commands[command.label] = command.action; | ||||
|                             }); | ||||
|                             self.devices[`Harmony-${dev.label}`] = { | ||||
|                                 id: dev.id, | ||||
|                                 name: dev.label, | ||||
|                                 commands: commands, | ||||
|                                 harmony: harmony, | ||||
|                                 hubType: "Harmony", | ||||
|                                 log: self.log, | ||||
|                                 on: false, | ||||
|                                 //Define device methods | ||||
|                                 supportsCommand(commandName: string): boolean { | ||||
|                                     let command = commands[commandName]; | ||||
|                                     return (command) ? true : false; | ||||
|                                 }, | ||||
|                                 getCommand(commandName: string): string { | ||||
|                                     return commands[commandName]; | ||||
|                                 }, | ||||
|                                 async powerOn(): Promise<void> { | ||||
|                                     let powerOnCommand: string = "Power On"; | ||||
|                                     let powerToggleCommand: string = "Power Toggle"; | ||||
|                                     if (this.supportsCommand(powerOnCommand)) { | ||||
|                                         await this.sendCommand(this.getCommand(powerOnCommand)); | ||||
|                                         this.on = true; | ||||
|                                     } else if (this.supportsCommand(powerToggleCommand)) { | ||||
|                                         await this.sendCommand(this.getCommand(powerToggleCommand)); | ||||
|                                         this.on = true; | ||||
|                                     } | ||||
|                                 }, | ||||
|                                 async powerOff(): Promise<void> { | ||||
|                                     let powerOffCommand: string = "Power Off"; | ||||
|                                     let powerToggleCommand: string = "Power Toggle"; | ||||
|                                     if (this.supportsCommand(powerOffCommand)) { | ||||
|                                         await this.sendCommand(this.getCommand(powerOffCommand)); | ||||
|                                         this.on = false; | ||||
|                                     } else if (this.supportsCommand(powerToggleCommand)) { | ||||
|                                         await this.sendCommand(this.getCommand(powerToggleCommand)); | ||||
|                                         this.on = false; | ||||
|                                     } | ||||
|                                 }, | ||||
|                                 async sendCommand(command: string): Promise<void> { | ||||
|                                     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}`); | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                         })); | ||||
|                     self.log(`Harmony data provider ready`); | ||||
|  | ||||
|                 } catch (err) { | ||||
|                     self.log(`ERROR - error connecting to harmony: ${err}`); | ||||
|                 } | ||||
|             } | ||||
|         }, 1000); | ||||
|     } | ||||
|  | ||||
|     private connectNeeo = async (address: string): Promise<void> => { | ||||
|  | ||||
|         let baseUrl = `http://${address}:3000/v1/projects/home`; | ||||
|  | ||||
|         let response: any = await fetch(`${baseUrl}/rooms`); | ||||
|         let rooms: { [key: string]: any } = await response.json(); | ||||
|  | ||||
|         function regularizeString(str: string) { | ||||
|             str = str.toLowerCase(); | ||||
|             return str.replace(/(?:^|\s)\S/g, function (a) { return a.toUpperCase(); }); | ||||
|         } | ||||
|  | ||||
|         for (let key in rooms) { | ||||
|             let devices: { [key: string]: any } = rooms[key].devices; | ||||
|             for (let deviceKey in devices) { | ||||
|                 let device = devices[deviceKey]; | ||||
|  | ||||
|                 //shape commands from macros | ||||
|                 let commands: { [item: string]: any } = {}; | ||||
|                 for (let macroKey in device.macros) { | ||||
|                     let macro: any = device.macros[macroKey]; | ||||
|                     commands[regularizeString(macro.name)] = { | ||||
|                         deviceKey: macro.deviceKey, | ||||
|                         macroKey: macro.key, | ||||
|                         roomKey: macro.roomKey, | ||||
|                     }; | ||||
|                 } | ||||
|  | ||||
|                 this.devices[`Neeo-${deviceKey}`] = { | ||||
|                     id: device.key, | ||||
|                     name: device.name, | ||||
|                     log: this.log, | ||||
|                     hubType: "Neeo", | ||||
|                     commands: commands, | ||||
|                     on: false, | ||||
|                     supportsCommand(commandName: string): boolean { | ||||
|                         let command = this.commands[commandName]; | ||||
|                         return (command) ? true : false; | ||||
|                     }, | ||||
|  | ||||
|                     getCommand(commandName: string): string { | ||||
|                         return this.commands[commandName]; | ||||
|                     }, | ||||
|  | ||||
|                     async sendCommand(command: string): Promise<void> { | ||||
|                         if (this.supportsCommand(command)) { | ||||
|                             let macro: INeeoMacro = this.commands[command]; | ||||
|                             let url: string = `${baseUrl}/rooms/${macro.roomKey}/devices/${macro.deviceKey}/macros/${macro.macroKey}/trigger`; | ||||
|                             this.log(`Sending command ${url}`); | ||||
|                             fetch(`${baseUrl}/rooms/${macro.roomKey}/devices/${macro.deviceKey}/macros/${macro.macroKey}/trigger`); | ||||
|                         } | ||||
|                     }, | ||||
|  | ||||
|                     async powerOn(): Promise<void> { | ||||
|                         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<void> { | ||||
|                         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; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| export default HubDataProvider; | ||||
							
								
								
									
										16
									
								
								src/DataProviders/IDevice.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/DataProviders/IDevice.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| import { hub } from ".."; | ||||
|  | ||||
| export interface IDevice { | ||||
|     id: string, | ||||
|     name: string, | ||||
|     harmony?: any, | ||||
|     log: any, | ||||
|     hubType: hub | ||||
|     supportsCommand(commandName: string): boolean, | ||||
|     getCommand(commandName: string): string, | ||||
|     sendCommand(command: string): void, | ||||
|     powerOn(): Promise<void>, | ||||
|     powerOff(): Promise<void>, | ||||
|     commands: { [name: string]: any }; | ||||
|     on: boolean; | ||||
| } | ||||
							
								
								
									
										69
									
								
								src/Models/Activity.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/Models/Activity.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| import { DeviceSetupItem } from './DeviceSetupItem'; | ||||
| /** | ||||
|  * Input properties. | ||||
|  */ | ||||
| export interface IActivityProps { | ||||
|     deviceList: Array<DeviceSetupItem>, | ||||
|     controlDeviceId: string, | ||||
|     volumeDeviceId: string, | ||||
|     outputDeviceId: string, | ||||
|     displayName: string | ||||
|     useMatrix: boolean, | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Data model class to hold activity related information. | ||||
|  */ | ||||
| export class Activity { | ||||
|     private _volumeDeviceId: string = ""; | ||||
|     private _outputDeviceId: string = ""; | ||||
|     private _controlDeviceId: string = ""; | ||||
|     private _displayName: string = ""; | ||||
|     private _deviceSetupItems: Array<DeviceSetupItem>; | ||||
|     private _useMatrix: boolean = false; | ||||
|  | ||||
|     constructor(props: IActivityProps) { | ||||
|         this._controlDeviceId = props.controlDeviceId; | ||||
|         this._outputDeviceId = props.outputDeviceId; | ||||
|         this._volumeDeviceId = props.volumeDeviceId; | ||||
|         this._displayName = props.displayName; | ||||
|         this._deviceSetupItems = props.deviceList; | ||||
|         this._useMatrix = props.useMatrix | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * The device associated with main control. | ||||
|      */ | ||||
|     public get controlDeviceId(): string { | ||||
|         return this._controlDeviceId; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * The device associated with the volume control. | ||||
|      */ | ||||
|     public get volumeDeviceId(): string { | ||||
|         return this._volumeDeviceId | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * The device associated with output. | ||||
|      */ | ||||
|     public get outputDeviceId(): string { | ||||
|         return this._outputDeviceId; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * The display name of the activity. | ||||
|      */ | ||||
|     public get displayName(): string { | ||||
|         return this._displayName; | ||||
|     } | ||||
|  | ||||
|     public get deviceSetupItems(): Array<DeviceSetupItem> { | ||||
|         return this._deviceSetupItems | ||||
|     } | ||||
|  | ||||
|     public get useMatrix(): boolean { | ||||
|         return this._useMatrix; | ||||
|     } | ||||
| } | ||||
| @@ -1,11 +0,0 @@ | ||||
| import { IDeviceSetupItem } from './IDeviceSetupItem'; | ||||
|  | ||||
| export interface IActivity { | ||||
|     OutputDevice: string; | ||||
|     VolumeDevice: string; | ||||
|     ControlDevice: string; | ||||
|     DisplayName: string; | ||||
|     DeviceSetupList: Array<IDeviceSetupItem>; | ||||
|     UseMatrix: boolean; | ||||
|  | ||||
| } | ||||
| @@ -1,20 +0,0 @@ | ||||
| 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; | ||||
|     Activities: Array<IActivity>; | ||||
| } | ||||
|  | ||||
| export interface IConfig { | ||||
|     hubIp: string; | ||||
|     EmitDevicesOnStartup: boolean; | ||||
|     Matrix: IMatrix; | ||||
|     ControlUnits: Array<IControlUnit>; | ||||
|     DeviceButtons: Array<IDeviceButton>; | ||||
|     Devices: Array<IDeviceConfig>; | ||||
|     Hubs: Array<IHub>; | ||||
| } | ||||
| @@ -1,7 +0,0 @@ | ||||
| export interface IDeviceButton { | ||||
|     DeviceName: string; | ||||
|     ButtonName: string; | ||||
|     DisplayName: string; | ||||
|     NumberOfKeyPresses: number; | ||||
|     IsStateful: boolean; | ||||
| } | ||||
| @@ -1,4 +0,0 @@ | ||||
| export interface IDeviceConfig { | ||||
|     Name: string; | ||||
|     Hub: string; | ||||
| } | ||||
| @@ -1,5 +0,0 @@ | ||||
|  | ||||
| export interface IDeviceSetupItem { | ||||
|     DeviceName: string; | ||||
|     Input: string; | ||||
| } | ||||
| @@ -1,5 +0,0 @@ | ||||
| export interface IHub { | ||||
|     Name: string; | ||||
|     Ip: string; | ||||
|     Harmony: any; | ||||
| } | ||||
| @@ -1,15 +0,0 @@ | ||||
| export interface IInput { | ||||
|     InputNumber: string, | ||||
|     InputDevice: string, | ||||
| } | ||||
|  | ||||
| export interface IOutput { | ||||
|     OutputLetter: string, | ||||
|     OutputDevice: string, | ||||
| } | ||||
|  | ||||
| export interface IMatrix { | ||||
|     Inputs: Array<IInput>; | ||||
|     Outputs: Array<IOutput>; | ||||
|     DeviceName: string; | ||||
| } | ||||
| @@ -1,5 +0,0 @@ | ||||
| export * from './IActivity'; | ||||
| export * from './IConfig'; | ||||
| export * from './IDeviceButton'; | ||||
| export * from './IDeviceSetupItem'; | ||||
| export * from './IMatrix'; | ||||
							
								
								
									
										33
									
								
								src/Models/DeviceSetupItem.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/Models/DeviceSetupItem.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| import { hub } from ".."; | ||||
|  | ||||
| export interface IDeviceSetupItemProps { | ||||
|     deviceName: string, | ||||
|     input: string, | ||||
|     hub: hub | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Data model to hold device setup items. | ||||
|  */ | ||||
| export class DeviceSetupItem { | ||||
|     private _deviceId: string = ""; | ||||
|     private _input: string = ""; | ||||
|     private _hubType: hub; | ||||
|     constructor(props: IDeviceSetupItemProps) { | ||||
|         this._deviceId = props.deviceName; | ||||
|         this._input = props.input; | ||||
|         this._hubType = props.hub; | ||||
|     } | ||||
|  | ||||
|     public get deviceName() { | ||||
|         return this._deviceId; | ||||
|     } | ||||
|  | ||||
|     public get input() { | ||||
|         return this._input; | ||||
|     } | ||||
|  | ||||
|     public get hubType() { | ||||
|         return this._hubType; | ||||
|     } | ||||
| } | ||||
| @@ -1,94 +0,0 @@ | ||||
| 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; | ||||
|  | ||||
|     public get on(): boolean { | ||||
|         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]; | ||||
|         return (command) ? true : false; | ||||
|     } | ||||
|  | ||||
|     public getCommand(commandName: string): ICommand { | ||||
|         return this._commands[commandName]; | ||||
|     } | ||||
|  | ||||
|     public async powerOn(): Promise<void> { | ||||
|         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<void> { | ||||
|         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<void> { | ||||
|         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}`); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,68 +0,0 @@ | ||||
| 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; | ||||
|     private _name: string; | ||||
|  | ||||
|     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]; | ||||
|     } | ||||
|  | ||||
|     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.emit("Ready"); | ||||
|  | ||||
|         } catch (err) { | ||||
|             this._log(`ERROR - error connecting to harmony: ${err}`); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private connect = async (): Promise<void> => { | ||||
|         await this._harmony.Connect(this._ip); | ||||
|     } | ||||
| } | ||||
| @@ -1,19 +0,0 @@ | ||||
| export interface ICommand { | ||||
|     command?: string, | ||||
|     deviceId?: string, | ||||
|     type?: string | ||||
| } | ||||
|  | ||||
| export interface IDevice { | ||||
|     id: string; | ||||
|     name: string; | ||||
|     harmony: any; | ||||
|     log: any | ||||
|     supportsCommand(commandName: string): boolean, | ||||
|     getCommand(commandName: string): ICommand, | ||||
|     powerOn(): Promise<void>; | ||||
|     powerOff(): Promise<void>; | ||||
|     sendCommand(commandName: string): Promise<void>; | ||||
|     commands: { [name: string]: ICommand }; | ||||
|     on: boolean; | ||||
| } | ||||
							
								
								
									
										51
									
								
								src/Models/Matrix.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/Models/Matrix.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| import { hub } from ".."; | ||||
|  | ||||
| export interface IMatrixProps { | ||||
|     inputs: Array<Input>, | ||||
|     outputs: Array<Output>, | ||||
|     deviceName: string, | ||||
|     hubType: hub | ||||
| } | ||||
|  | ||||
| export interface Input { | ||||
|     inputNumber: string, | ||||
|     inputDevice: string, | ||||
| } | ||||
|  | ||||
| export interface Output { | ||||
|     outputLetter: string, | ||||
|     outputDevice: string, | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Data model to hold matrix information. | ||||
|  */ | ||||
| export class Matrix { | ||||
|     private _inputs: Array<Input> = []; | ||||
|     private _outputs: Array<Output> = []; | ||||
|     private _deviceName: string; | ||||
|     private _hubType: hub = "Harmony"; | ||||
|  | ||||
|     constructor(props: IMatrixProps) { | ||||
|         this._inputs = props.inputs; | ||||
|         this._outputs = props.outputs; | ||||
|         this._deviceName = props.deviceName; | ||||
|         this._hubType = props.hubType; | ||||
|     } | ||||
|  | ||||
|     public get inputs(): Array<Input> { | ||||
|         return this._inputs | ||||
|     } | ||||
|  | ||||
|     public get outputs(): Array<Output> { | ||||
|         return this._outputs; | ||||
|     } | ||||
|  | ||||
|     public get deviceName(): string { | ||||
|         return this._deviceName; | ||||
|     } | ||||
|  | ||||
|     public get hubType(): hub { | ||||
|         return this._hubType; | ||||
|     } | ||||
| } | ||||
| @@ -1 +0,0 @@ | ||||
| export * from './IDevice'; | ||||
							
								
								
									
										187
									
								
								src/index.ts
									
									
									
									
									
								
							
							
						
						
									
										187
									
								
								src/index.ts
									
									
									
									
									
								
							| @@ -1,13 +1,14 @@ | ||||
| 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"; | ||||
| import { HarmonyHub } from "./Models/HarmonyHub"; | ||||
| import { ControlUnit } from "./Accessories/ControlUnit"; | ||||
| import { Activity } from "./Models/Activity"; | ||||
| import { DeviceSetupItem } from "./Models/DeviceSetupItem"; | ||||
| import { Input, Output, Matrix } from "./Models/Matrix"; | ||||
| import HubDataProvider from './DataProviders/HubDataProvider'; | ||||
|  | ||||
| let Accessory: any; | ||||
| let Homebridge: any; | ||||
|  | ||||
| export type hub = "Harmony" | "Neeo"; | ||||
|  | ||||
| /** | ||||
|  * Main entry. | ||||
|  * @param homebridge  | ||||
| @@ -25,10 +26,10 @@ export default function (homebridge: any) { | ||||
|  | ||||
| class HarmonyMatrixPlatform { | ||||
|     log: any = {}; | ||||
|     config: Config.IConfig; | ||||
|     config: any = {}; | ||||
|     api: any; | ||||
|     dataProvider: HarmonyDataProvider | null; | ||||
|     accessoryList: Array<Accessories.IAccessory> = []; | ||||
|     externalAccessories: Array<any> = []; | ||||
|     dataProvider: HubDataProvider | null; | ||||
|  | ||||
|     constructor(log: any, config: any, api: any) { | ||||
|         this.log = log; | ||||
| @@ -38,37 +39,7 @@ class HarmonyMatrixPlatform { | ||||
|         this.api.on('didFinishLaunching', this.didFinishLaunching.bind(this)); | ||||
|  | ||||
|         this.dataProvider = null; | ||||
|  | ||||
|         if (this.config) { | ||||
|             //construct data provider | ||||
|             this.dataProvider = new HarmonyDataProvider({ | ||||
|                 hubs: this.config.Hubs, | ||||
|                 deviceConfigs: this.config.Devices, | ||||
|                 matrix: this.config.Matrix, | ||||
|                 log: this.log | ||||
|             }); | ||||
|  | ||||
|             //Emit devices if requested | ||||
|  | ||||
|             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; | ||||
|                         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}`); | ||||
|                             }); | ||||
|                         }); | ||||
|                     }); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|         } | ||||
|         this.log("This is new"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -79,44 +50,134 @@ class HarmonyMatrixPlatform { | ||||
|         this.log(`Publishing external accessories`); | ||||
|  | ||||
|         //This is required in order to have multiple tv remotes on one platform | ||||
|         this.accessoryList.forEach((accessory: Accessories.IAccessory) => { | ||||
|             if (accessory instanceof Accessories.ControlUnit) { | ||||
|                 this.api.publishExternalAccessories("HarmonyMatrixPlatform", [accessory.platformAccessory]); | ||||
|             } | ||||
|         this.externalAccessories.forEach((accessory: ControlUnit) => { | ||||
|             this.api.publishExternalAccessories("HarmonyMatrixPlatform", [accessory.platformAccessory]); | ||||
|         }) | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Called by homebridge to gather accessories. | ||||
|      * @param callback  | ||||
|      */ | ||||
|     accessories(callback: (accessories: Array<Accessories.IAccessory>) => void) { | ||||
|     accessories(callback: (accessories: Array<ControlUnit>) => void) { | ||||
|         //Parse ip | ||||
|         let harmonyIp: string = this.config["harmonyHubIp"]; | ||||
|         let neeoIp: string = this.config["neeoHubIp"]; | ||||
|  | ||||
|         //Parse matrix | ||||
|         let configInputs: any = this.config["Matrix"]["Inputs"]; | ||||
|         let configOutputs: any = this.config["Matrix"]["Outputs"]; | ||||
|         let matrixName: string = this.config["Matrix"]["DeviceName"]; | ||||
|         let hub: hub = this.config["Matrix"]["Hub"]; | ||||
|  | ||||
|         let inputs: Array<Input> = []; | ||||
|         let outputs: Array<Output> = []; | ||||
|  | ||||
|         configInputs.forEach((configInput: any) => { | ||||
|             let inputDevice: string = configInput["InputDevice"]; | ||||
|             let inputNumber: string = configInput["InputNumber"]; | ||||
|  | ||||
|             this.log(`INFO - Added input to matrix '${inputDevice}'`); | ||||
|  | ||||
|             inputs.push({ | ||||
|                 inputDevice: inputDevice, | ||||
|                 inputNumber: inputNumber | ||||
|             }); | ||||
|         }); | ||||
|         configOutputs.forEach((configOutput: any) => { | ||||
|             let outputDevice: string = configOutput["OutputDevice"]; | ||||
|             let outputLetter: string = configOutput["OutputLetter"]; | ||||
|  | ||||
|             this.log(`INFO - Added output to matrix '${outputDevice}'`); | ||||
|  | ||||
|             outputs.push({ | ||||
|                 outputDevice: outputDevice, | ||||
|                 outputLetter: outputLetter | ||||
|             }); | ||||
|  | ||||
|         //Add control units | ||||
|         this.config.ControlUnits.forEach((unit: Config.IControlUnit) => { | ||||
|             this.accessoryList.push(new Accessories.ControlUnit({ | ||||
|                 dataProvider: this.dataProvider!, | ||||
|                 displayName: unit.DisplayName, | ||||
|                 api: this.api, | ||||
|                 log: this.log, | ||||
|                 activities: unit.Activities, | ||||
|                 homebridge: Homebridge, | ||||
|             })); | ||||
|         }); | ||||
|  | ||||
|         //Add device buttons | ||||
|         this.config.DeviceButtons.forEach((button: Config.IDeviceButton) => { | ||||
|             this.accessoryList.push(new Accessories.DeviceButton({ | ||||
|         let matrix = new Matrix({ | ||||
|             inputs: inputs, | ||||
|             outputs: outputs, | ||||
|             deviceName: matrixName, | ||||
|             hubType: hub | ||||
|         }); | ||||
|  | ||||
|         //construct data provider | ||||
|         this.dataProvider = new HubDataProvider({ | ||||
|             harmonyHubAddress: harmonyIp, | ||||
|             neeoHubAddress: neeoIp, | ||||
|             matrix: matrix, | ||||
|             log: this.log | ||||
|         }); | ||||
|  | ||||
|         //Parse control units | ||||
|         let configControlUnits: any = this.config["ControlUnits"]; | ||||
|         let controlUnits: Array<ControlUnit> = []; | ||||
|         configControlUnits.forEach((configControlUnit: any) => { | ||||
|             //Parse activities list | ||||
|             let configActivities: any = configControlUnit["Activities"]; | ||||
|             let activities: Array<Activity> = []; | ||||
|             configActivities.forEach((configActivity: any) => { | ||||
|                 //parse devices | ||||
|                 let configDevices: any = configActivity["DeviceSetupList"]; | ||||
|                 let devices: Array<DeviceSetupItem> = []; | ||||
|                 configDevices.forEach((configDevice: any) => { | ||||
|                     //Add device | ||||
|                     devices.push(new DeviceSetupItem({ | ||||
|                         deviceName: configDevice["DeviceName"], | ||||
|                         input: configDevice["Input"], | ||||
|                         hub: configDevice["Hub"] | ||||
|                     })); | ||||
|                     this.log(`INFO - Added device '${configDevice["DeviceName"]}' for activity '${configActivity["DisplayName"]}'`); | ||||
|                 }); | ||||
|  | ||||
|                 //Add activity | ||||
|                 activities.push(new Activity({ | ||||
|                     volumeDeviceId: configActivity["VolumeDevice"], | ||||
|                     controlDeviceId: configActivity["ControlDevice"], | ||||
|                     outputDeviceId: configActivity["OutputDevice"], | ||||
|                     displayName: configActivity["DisplayName"], | ||||
|                     useMatrix: configActivity["UseMatrix"], | ||||
|                     deviceList: devices | ||||
|                 })); | ||||
|                 this.log(`INFO - Added activity '${configActivity["DisplayName"]}'`); | ||||
|             }); | ||||
|  | ||||
|             let controlUnit: ControlUnit = new ControlUnit({ | ||||
|                 dataProvider: this.dataProvider!, | ||||
|                 buttonName: button.ButtonName, | ||||
|                 displayName: button.DisplayName, | ||||
|                 deviceInfo: button, | ||||
|                 displayName: configControlUnit["DisplayName"], | ||||
|                 api: this.api, | ||||
|                 log: this.log, | ||||
|                 activities: activities, | ||||
|                 homebridge: Homebridge, | ||||
|                 isExternal: false | ||||
|             }); | ||||
|  | ||||
|             })) | ||||
|             let controlUnitExternal: ControlUnit = new ControlUnit({ | ||||
|                 dataProvider: this.dataProvider!, | ||||
|                 displayName: `${configControlUnit["DisplayName"]}`, | ||||
|                 api: this.api, | ||||
|                 log: this.log, | ||||
|                 activities: activities, | ||||
|                 homebridge: Homebridge, | ||||
|                 isExternal: true | ||||
|             }); | ||||
|  | ||||
|             //@ts-ignore | ||||
|             let accessory = controlUnit as homebridge.platformAccessory; | ||||
|             //@ts-ignore | ||||
|             let externalAccessory = controlUnitExternal as homebridge.platformAccessory; | ||||
|  | ||||
|             //Add control unit | ||||
|             controlUnits.push(accessory); | ||||
|             //Add to list of remotes | ||||
|             this.externalAccessories.push(externalAccessory); | ||||
|  | ||||
|             this.log(`INFO - Added ControlUnit`); | ||||
|         }); | ||||
|         callback(this.accessoryList); | ||||
|         callback(controlUnits); | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user