Compare commits
	
		
			11 Commits
		
	
	
		
			1.0.0
			...
			feature/ne
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | ed0b418b4d | ||
|  | 2475aea1c8 | ||
|  | 31c5c5b9e2 | ||
|  | a539287b9e | ||
|  | 73cba09edb | ||
|  | 9b48614dd1 | ||
|  | 04024d565d | ||
|  | 7638644935 | ||
|  | f0eb57e4dc | ||
|  | 0ea4188487 | ||
|  | 8bb65b5b3c | 
							
								
								
									
										16
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | { | ||||||
|  |     // Use IntelliSense to learn about possible attributes. | ||||||
|  |     // Hover to view descriptions of existing attributes. | ||||||
|  |     // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||||||
|  |     "version": "0.2.0", | ||||||
|  |     "configurations": [ | ||||||
|  |         { | ||||||
|  |             "type": "node", | ||||||
|  |             "request": "launch", | ||||||
|  |             "name": "Launch Program", | ||||||
|  |             "preLaunchTask": "build and install", | ||||||
|  |             "program": "/Users/brandonwatson/.npm-global/bin/homebridge", | ||||||
|  |             "sourceMaps": true, | ||||||
|  |         } | ||||||
|  |     ] | ||||||
|  | } | ||||||
							
								
								
									
										17
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | { | ||||||
|  |     // See https://go.microsoft.com/fwlink/?LinkId=733558  | ||||||
|  |     // for the documentation about the tasks.json format | ||||||
|  |     "version": "2.0.0", | ||||||
|  |     "tasks": [ | ||||||
|  |         { | ||||||
|  |             "type": "npm", | ||||||
|  |             "script": "build", | ||||||
|  |             "problemMatcher": [] | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             "type": "shell", | ||||||
|  |             "label": "build and install", | ||||||
|  |             "command": "npm run build&&sudo npm install -g --unsafe-perm ." | ||||||
|  |         } | ||||||
|  |     ] | ||||||
|  | } | ||||||
							
								
								
									
										51
									
								
								config.json
									
									
									
									
									
								
							
							
						
						
									
										51
									
								
								config.json
									
									
									
									
									
								
							| @@ -13,8 +13,10 @@ | |||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|             "platform": "HarmonyHubMatrix", |             "platform": "HarmonyHubMatrix", | ||||||
|             "hubIp": "192.168.1.14", |             "harmonyHubIp": "192.168.1.14", | ||||||
|  |             "neeoHubIp": "192.168.1.24", | ||||||
|             "Matrix": { |             "Matrix": { | ||||||
|  |                 "Hub": "Harmony", | ||||||
|                 "DeviceName": "Gefen AV Switch", |                 "DeviceName": "Gefen AV Switch", | ||||||
|                 "Inputs": [ |                 "Inputs": [ | ||||||
|                     { |                     { | ||||||
| @@ -57,14 +59,17 @@ | |||||||
|                             "DisplayName": "Chromecast", |                             "DisplayName": "Chromecast", | ||||||
|                             "DeviceSetupList": [ |                             "DeviceSetupList": [ | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|                                     "DeviceName": "LG TV", |                                     "DeviceName": "LG TV", | ||||||
|                                     "Input": "HDMI1" |                                     "Input": "HDMI1" | ||||||
|                                 }, |                                 }, | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|                                     "DeviceName": "JVC AV Receiver", |                                     "DeviceName": "JVC AV Receiver", | ||||||
|                                     "Input": "DVD" |                                     "Input": "DVD" | ||||||
|                                 }, |                                 }, | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|                                     "DeviceName": "Chromecast", |                                     "DeviceName": "Chromecast", | ||||||
|                                     "Input": [] |                                     "Input": [] | ||||||
|                                 } |                                 } | ||||||
| @@ -78,14 +83,17 @@ | |||||||
|                             "DisplayName": "Play Xbox One", |                             "DisplayName": "Play Xbox One", | ||||||
|                             "DeviceSetupList": [ |                             "DeviceSetupList": [ | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|                                     "DeviceName": "Microsoft Xbox One", |                                     "DeviceName": "Microsoft Xbox One", | ||||||
|                                     "Input": [] |                                     "Input": [] | ||||||
|                                 }, |                                 }, | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|                                     "DeviceName": "LG TV", |                                     "DeviceName": "LG TV", | ||||||
|                                     "Input": "HDMI1" |                                     "Input": "HDMI1" | ||||||
|                                 }, |                                 }, | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|                                     "DeviceName": "JVC AV Receiver", |                                     "DeviceName": "JVC AV Receiver", | ||||||
|                                     "Input": "DVD" |                                     "Input": "DVD" | ||||||
|                                 } |                                 } | ||||||
| @@ -99,14 +107,17 @@ | |||||||
|                             "DisplayName": "Play PS4", |                             "DisplayName": "Play PS4", | ||||||
|                             "DeviceSetupList": [ |                             "DeviceSetupList": [ | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|                                     "DeviceName": "Sony PS4", |                                     "DeviceName": "Sony PS4", | ||||||
|                                     "Input": [] |                                     "Input": [] | ||||||
|                                 }, |                                 }, | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|                                     "DeviceName": "LG TV", |                                     "DeviceName": "LG TV", | ||||||
|                                     "Input": "HDMI1" |                                     "Input": "HDMI1" | ||||||
|                                 }, |                                 }, | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|                                     "DeviceName": "JVC AV Receiver", |                                     "DeviceName": "JVC AV Receiver", | ||||||
|                                     "Input": "DVD" |                                     "Input": "DVD" | ||||||
|                                 } |                                 } | ||||||
| @@ -120,14 +131,17 @@ | |||||||
|                             "DisplayName": "Apple TV", |                             "DisplayName": "Apple TV", | ||||||
|                             "DeviceSetupList": [ |                             "DeviceSetupList": [ | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|                                     "DeviceName": "Apple TV Gen 4", |                                     "DeviceName": "Apple TV Gen 4", | ||||||
|                                     "Input": [] |                                     "Input": [] | ||||||
|                                 }, |                                 }, | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|                                     "DeviceName": "LG TV", |                                     "DeviceName": "LG TV", | ||||||
|                                     "Input": "HDMI1" |                                     "Input": "HDMI1" | ||||||
|                                 }, |                                 }, | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|                                     "DeviceName": "JVC AV Receiver", |                                     "DeviceName": "JVC AV Receiver", | ||||||
|                                     "Input": "DVD" |                                     "Input": "DVD" | ||||||
|                                 } |                                 } | ||||||
| @@ -141,6 +155,7 @@ | |||||||
|                             "DisplayName": "Listen to Music", |                             "DisplayName": "Listen to Music", | ||||||
|                             "DeviceSetupList": [ |                             "DeviceSetupList": [ | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|                                     "DeviceName": "JVC AV Receiver", |                                     "DeviceName": "JVC AV Receiver", | ||||||
|                                     "Input": "DVD" |                                     "Input": "DVD" | ||||||
|                                 } |                                 } | ||||||
| @@ -159,19 +174,22 @@ | |||||||
|                             "DisplayName": "Chromecast", |                             "DisplayName": "Chromecast", | ||||||
|                             "DeviceSetupList": [ |                             "DeviceSetupList": [ | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Neeo", | ||||||
|                                     "DeviceName": "Vizio TV", |                                     "DeviceName": "Vizio TV", | ||||||
|                                     "Input": "HDMI1" |                                     "Input": "HDMI1" | ||||||
|                                 }, |                                 }, | ||||||
|                                 { |                                 { | ||||||
|                                     "DeviceName": "Harman Kardon AV Receiver", |                                     "Hub": "Neeo", | ||||||
|  |                                     "DeviceName": "Harmon Kardon Receicer", | ||||||
|                                     "Input": "Game" |                                     "Input": "Game" | ||||||
|                                 }, |                                 }, | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|                                     "DeviceName": "Chromecast", |                                     "DeviceName": "Chromecast", | ||||||
|                                     "Input": [] |                                     "Input": [] | ||||||
|                                 } |                                 } | ||||||
|                             ], |                             ], | ||||||
|                             "VolumeDevice": "Harman Kardon AV Receiver", |                             "VolumeDevice": "Harmon Kardon Receicer", | ||||||
|                             "ControlDevice": "Chromecast", |                             "ControlDevice": "Chromecast", | ||||||
|                             "OutputDevice": "Vizio TV", |                             "OutputDevice": "Vizio TV", | ||||||
|                             "UseMatrix": "true" |                             "UseMatrix": "true" | ||||||
| @@ -180,19 +198,22 @@ | |||||||
|                             "DisplayName": "Play Xbox One", |                             "DisplayName": "Play Xbox One", | ||||||
|                             "DeviceSetupList": [ |                             "DeviceSetupList": [ | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|                                     "DeviceName": "Microsoft Xbox One", |                                     "DeviceName": "Microsoft Xbox One", | ||||||
|                                     "Input": [] |                                     "Input": [] | ||||||
|                                 }, |                                 }, | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Neeo", | ||||||
|                                     "DeviceName": "Vizio TV", |                                     "DeviceName": "Vizio TV", | ||||||
|                                     "Input": "HDMI1" |                                     "Input": "HDMI1" | ||||||
|                                 }, |                                 }, | ||||||
|                                 { |                                 { | ||||||
|                                     "DeviceName": "Harman Kardon AV Receiver", |                                     "Hub": "Neeo", | ||||||
|  |                                     "DeviceName": "Harmon Kardon Receicer", | ||||||
|                                     "Input": "Game" |                                     "Input": "Game" | ||||||
|                                 } |                                 } | ||||||
|                             ], |                             ], | ||||||
|                             "VolumeDevice": "Harman Kardon AV Receiver", |                             "VolumeDevice": "Harmon Kardon Receicer", | ||||||
|                             "ControlDevice": "Microsoft Xbox One", |                             "ControlDevice": "Microsoft Xbox One", | ||||||
|                             "OutputDevice": "Vizio TV", |                             "OutputDevice": "Vizio TV", | ||||||
|                             "UseMatrix": "true" |                             "UseMatrix": "true" | ||||||
| @@ -201,19 +222,22 @@ | |||||||
|                             "DisplayName": "Play PS4", |                             "DisplayName": "Play PS4", | ||||||
|                             "DeviceSetupList": [ |                             "DeviceSetupList": [ | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|                                     "DeviceName": "Sony PS4", |                                     "DeviceName": "Sony PS4", | ||||||
|                                     "Input": [] |                                     "Input": [] | ||||||
|                                 }, |                                 }, | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Neeo", | ||||||
|                                     "DeviceName": "Vizio TV", |                                     "DeviceName": "Vizio TV", | ||||||
|                                     "Input": "HDMI1" |                                     "Input": "HDMI1" | ||||||
|                                 }, |                                 }, | ||||||
|                                 { |                                 { | ||||||
|                                     "DeviceName": "Harman Kardon AV Receiver", |                                     "Hub": "Neeo", | ||||||
|  |                                     "DeviceName": "Harmon Kardon Receicer", | ||||||
|                                     "Input": "DVD" |                                     "Input": "DVD" | ||||||
|                                 } |                                 } | ||||||
|                             ], |                             ], | ||||||
|                             "VolumeDevice": "Harman Kardon AV Receiver", |                             "VolumeDevice": "Harmon Kardon Receicer", | ||||||
|                             "ControlDevice": "Sony PS4", |                             "ControlDevice": "Sony PS4", | ||||||
|                             "OutputDevice": "LG TV", |                             "OutputDevice": "LG TV", | ||||||
|                             "UseMatrix": "true" |                             "UseMatrix": "true" | ||||||
| @@ -222,19 +246,22 @@ | |||||||
|                             "DisplayName": "Apple TV", |                             "DisplayName": "Apple TV", | ||||||
|                             "DeviceSetupList": [ |                             "DeviceSetupList": [ | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|                                     "DeviceName": "Apple TV Gen 4", |                                     "DeviceName": "Apple TV Gen 4", | ||||||
|                                     "Input": [] |                                     "Input": [] | ||||||
|                                 }, |                                 }, | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Neeo", | ||||||
|                                     "DeviceName": "Vizio TV", |                                     "DeviceName": "Vizio TV", | ||||||
|                                     "Input": "HDMI1" |                                     "Input": "HDMI1" | ||||||
|                                 }, |                                 }, | ||||||
|                                 { |                                 { | ||||||
|                                     "DeviceName": "Harman Kardon AV Receiver", |                                     "Hub": "Neeo", | ||||||
|  |                                     "DeviceName": "Harmon Kardon Receicer", | ||||||
|                                     "Input": "Game" |                                     "Input": "Game" | ||||||
|                                 } |                                 } | ||||||
|                             ], |                             ], | ||||||
|                             "VolumeDevice": "Harman Kardon AV Receiver", |                             "VolumeDevice": "Harmon Kardon Receicer", | ||||||
|                             "ControlDevice": "Apple TV Gen 4", |                             "ControlDevice": "Apple TV Gen 4", | ||||||
|                             "OutputDevice": "Vizio TV", |                             "OutputDevice": "Vizio TV", | ||||||
|                             "UseMatrix": "true" |                             "UseMatrix": "true" | ||||||
| @@ -248,10 +275,12 @@ | |||||||
|                             "DisplayName": "Chromecast", |                             "DisplayName": "Chromecast", | ||||||
|                             "DeviceSetupList": [ |                             "DeviceSetupList": [ | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|                                     "DeviceName": "Chromecast", |                                     "DeviceName": "Chromecast", | ||||||
|                                     "Input": [] |                                     "Input": [] | ||||||
|                                 }, |                                 }, | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|                                     "DeviceName": "Westinghouse TV", |                                     "DeviceName": "Westinghouse TV", | ||||||
|                                     "Input": "HDMI1" |                                     "Input": "HDMI1" | ||||||
|                                 } |                                 } | ||||||
| @@ -265,10 +294,12 @@ | |||||||
|                             "DisplayName": "Watch Apple Tv", |                             "DisplayName": "Watch Apple Tv", | ||||||
|                             "DeviceSetupList": [ |                             "DeviceSetupList": [ | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|                                     "DeviceName": "Apple TV Gen 4", |                                     "DeviceName": "Apple TV Gen 4", | ||||||
|                                     "Input": [] |                                     "Input": [] | ||||||
|                                 }, |                                 }, | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|                                     "DeviceName": "Westinghouse TV", |                                     "DeviceName": "Westinghouse TV", | ||||||
|                                     "Input": "HDMI1" |                                     "Input": "HDMI1" | ||||||
|                                 } |                                 } | ||||||
| @@ -282,10 +313,12 @@ | |||||||
|                             "DisplayName": "Play Xbox One", |                             "DisplayName": "Play Xbox One", | ||||||
|                             "DeviceSetupList": [ |                             "DeviceSetupList": [ | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|                                     "DeviceName": "Westinghouse TV", |                                     "DeviceName": "Westinghouse TV", | ||||||
|                                     "Input": "HDMI1" |                                     "Input": "HDMI1" | ||||||
|                                 }, |                                 }, | ||||||
|                                 { |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|                                     "DeviceName": "Microsoft Xbox One", |                                     "DeviceName": "Microsoft Xbox One", | ||||||
|                                     "Input": [] |                                     "Input": [] | ||||||
|                                 } |                                 } | ||||||
|   | |||||||
							
								
								
									
										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", |       "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.7.tgz", | ||||||
|       "integrity": "sha512-1YKeT4JitGgE4SOzyB9eMwO0nGVNkNEsm9qlIt1Lqm/tG2QEiSMTD4kS3aO6L+w5SClLVxALmIBESK6Mk5wX0A==" |       "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": { |     "ajv": { | ||||||
|       "version": "6.10.0", |       "version": "6.10.0", | ||||||
|       "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", |       "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", |       "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", | ||||||
|       "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" |       "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": { |     "node-persist": { | ||||||
|       "version": "0.0.8", |       "version": "0.0.8", | ||||||
|       "resolved": "https://registry.npmjs.org/node-persist/-/node-persist-0.0.8.tgz", |       "resolved": "https://registry.npmjs.org/node-persist/-/node-persist-0.0.8.tgz", | ||||||
|   | |||||||
| @@ -35,8 +35,10 @@ | |||||||
|         "@babel/preset-env": "^7.4.5", |         "@babel/preset-env": "^7.4.5", | ||||||
|         "@babel/preset-typescript": "^7.3.3", |         "@babel/preset-typescript": "^7.3.3", | ||||||
|         "@types/node": "^12.0.7", |         "@types/node": "^12.0.7", | ||||||
|  |         "@types/node-fetch": "^2.5.0", | ||||||
|         "harmony-websocket": "^1.1.0", |         "harmony-websocket": "^1.1.0", | ||||||
|         "homebridge": "^0.4.50", |         "homebridge": "^0.4.50", | ||||||
|  |         "node-fetch": "^2.6.0", | ||||||
|         "request": "^2.88.0" |         "request": "^2.88.0" | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -2,20 +2,40 @@ import { Activity } from '../Models/Activity'; | |||||||
| import { Matrix } from '../Models/Matrix'; | import { Matrix } from '../Models/Matrix'; | ||||||
| import { IAccessory } from './IAccessory'; | import { IAccessory } from './IAccessory'; | ||||||
| import callbackify from '../Util/Callbackify'; | import callbackify from '../Util/Callbackify'; | ||||||
| import HarmonyDataProvider from '../DataProviders/HarmonyDataProvider'; | import HubDataProvider from '../DataProviders/HubDataProvider'; | ||||||
|  |  | ||||||
| let Service: HAPNodeJS.Service; | let Service: HAPNodeJS.Service; | ||||||
| let Characteristic: HAPNodeJS.Characteristic; | let Characteristic: HAPNodeJS.Characteristic; | ||||||
| let Api: any; | let Api: any; | ||||||
| let homebridge: any; | let homebridge: any; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Enum describing remote key presses from homebridge. | ||||||
|  |  */ | ||||||
|  | export enum RemoteKey { | ||||||
|  |     REWIND = 0, | ||||||
|  |     FAST_FORWARD = 1, | ||||||
|  |     NEXT_TRACK = 2, | ||||||
|  |     PREVIOUS_TRACK = 3, | ||||||
|  |     ARROW_UP = 4, | ||||||
|  |     ARROW_DOWN = 5, | ||||||
|  |     ARROW_LEFT = 6, | ||||||
|  |     ARROW_RIGHT = 7, | ||||||
|  |     SELECT = 8, | ||||||
|  |     BACK = 9, | ||||||
|  |     EXIT = 10, | ||||||
|  |     PLAY_PAUSE = 11, | ||||||
|  |     INFORMATION = 15, | ||||||
|  | } | ||||||
|  |  | ||||||
| export interface IControlUnitProps { | export interface IControlUnitProps { | ||||||
|     dataProvider: HarmonyDataProvider, |     dataProvider: HubDataProvider, | ||||||
|     displayName: string, |     displayName: string, | ||||||
|     activities: Array<Activity>, |     activities: Array<Activity>, | ||||||
|     api: any, |     api: any, | ||||||
|     log: any, |     log: any, | ||||||
|     homebridge: any, |     homebridge: any, | ||||||
|  |     isExternal: boolean, | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -28,6 +48,7 @@ export class ControlUnit implements IAccessory { | |||||||
|     //fields |     //fields | ||||||
|     private log: any = {}; |     private log: any = {}; | ||||||
|     private displayName: string = ""; |     private displayName: string = ""; | ||||||
|  |     private isExternal: boolean = false; | ||||||
|  |  | ||||||
|     //Service fields |     //Service fields | ||||||
|     private televisionService: HAPNodeJS.Service | undefined; |     private televisionService: HAPNodeJS.Service | undefined; | ||||||
| @@ -37,7 +58,7 @@ export class ControlUnit implements IAccessory { | |||||||
|  |  | ||||||
|     //Harmony fields |     //Harmony fields | ||||||
|     private activities: Array<Activity> = []; |     private activities: Array<Activity> = []; | ||||||
|     private dataProvider: HarmonyDataProvider; |     private dataProvider: HubDataProvider; | ||||||
|  |  | ||||||
|     public platformAccessory: any; |     public platformAccessory: any; | ||||||
|  |  | ||||||
| @@ -51,19 +72,23 @@ export class ControlUnit implements IAccessory { | |||||||
|         Api = props.api; |         Api = props.api; | ||||||
|         Service = props.api.hap.Service; |         Service = props.api.hap.Service; | ||||||
|         Characteristic = props.api.hap.Characteristic; |         Characteristic = props.api.hap.Characteristic; | ||||||
|         this.displayName = props.displayName; |         this.name = props.displayName; | ||||||
|         this.name = this.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; |         homebridge = props.homebridge; | ||||||
|  |  | ||||||
|         this.platformAccessory = new homebridge.platformAccessory(this.name, this.generateUUID(), homebridge.hap.Accessory.Categories.TELEVISION); |         this.platformAccessory = new homebridge.platformAccessory(this.displayName, this.generateUUID(), homebridge.hap.Accessory.Categories.TELEVISION); | ||||||
|  |  | ||||||
|         //Configure services |         //Configure services | ||||||
|         this.configureTvService(); |         this.configureTvService(); | ||||||
|  |  | ||||||
|         this.configureTvSpeakerService(); |         this.configureTvSpeakerService(); | ||||||
|  |  | ||||||
|  |  | ||||||
|         // this.configureAccessoryInformation(); |         // this.configureAccessoryInformation(); | ||||||
|         this.configureInputSourceService(); |         this.configureInputSourceService(); | ||||||
|  |  | ||||||
| @@ -108,9 +133,12 @@ export class ControlUnit implements IAccessory { | |||||||
|             //@ts-ignore |             //@ts-ignore | ||||||
|             .on("get", callbackify(this.onGetAccessoryActive)); |             .on("get", callbackify(this.onGetAccessoryActive)); | ||||||
|  |  | ||||||
|         this.televisionService.getCharacteristic(Characteristic.RemoteKey) |         //Set remote characteristics if is external | ||||||
|             //@ts-ignore |         if (this.isExternal) { | ||||||
|             .on("set", callbackify(this.onSetRemoteKey)); |             this.televisionService.getCharacteristic(Characteristic.RemoteKey) | ||||||
|  |                 //@ts-ignore | ||||||
|  |                 .on("set", callbackify(this.onSetRemoteKey)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         this.televisionService.getCharacteristic(Characteristic.ActiveIdentifier) |         this.televisionService.getCharacteristic(Characteristic.ActiveIdentifier) | ||||||
|             //@ts-ignore |             //@ts-ignore | ||||||
| @@ -123,10 +151,12 @@ export class ControlUnit implements IAccessory { | |||||||
|      * Event handler for SET active characteristic |      * Event handler for SET active characteristic | ||||||
|      */ |      */ | ||||||
|     private onSetAccessoryActive = async (value: any) => { |     private onSetAccessoryActive = async (value: any) => { | ||||||
|         switch (value) { |         if (!this.isExternal) { | ||||||
|             case 0: this.dataProvider.powerOff(this.name); break; |             switch (value) { | ||||||
|             //Turn on with first activity |                 case 0: this.dataProvider.powerOff(this.name); break; | ||||||
|             case 1: this.dataProvider.powerOn(this.name, this.activities[0]); break; |                 //Turn on with first activity | ||||||
|  |                 case 1: this.dataProvider.powerOn(this.name, this.activities[0]); break; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -135,21 +165,25 @@ export class ControlUnit implements IAccessory { | |||||||
|      */ |      */ | ||||||
|     private onGetAccessoryActive = async () => { |     private onGetAccessoryActive = async () => { | ||||||
|         //@ts-ignore |         //@ts-ignore | ||||||
|         return this.dataProvider.getIsActive ? Characteristic.Active.Active : Characteristic.Active.Inactive |         return this.dataProvider.getIsActive() ? Characteristic.Active.Active : Characteristic.Active.Inactive | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Event handler for SET remote key |      * Event handler for SET remote key | ||||||
|      */ |      */ | ||||||
|     private onSetRemoteKey = async (key: any) => { |     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 |      * Event handler for SET active identifier characteristic | ||||||
|      */ |      */ | ||||||
|     private onSetActiveIdentifier = async (identifier: any) => { |     private onSetActiveIdentifier = async (identifier: any) => { | ||||||
|         this.dataProvider.startActivity(this.name, this.activities[identifier]); |         if (!this.isExternal) { | ||||||
|  |             this.dataProvider.startActivity(this.name, this.activities[identifier]); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -199,9 +233,11 @@ export class ControlUnit implements IAccessory { | |||||||
|      * Event handler for SET volume characteristic |      * Event handler for SET volume characteristic | ||||||
|      */ |      */ | ||||||
|     private onSetVolumeSelector = async (value: any) => { |     private onSetVolumeSelector = async (value: any) => { | ||||||
|         switch (value) { |         if (this.isExternal) { | ||||||
|             case 0: this.dataProvider.volumeUp(this.name); break; |             switch (value) { | ||||||
|             case 1: this.dataProvider.volumeDown(this.name); break; |                 case 0: this.dataProvider.volumeUp(this.name); break; | ||||||
|  |                 case 1: this.dataProvider.volumeDown(this.name); break; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -266,8 +302,6 @@ export class ControlUnit implements IAccessory { | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Called by homebridge to gather services for this accessory. |      * Called by homebridge to gather services for this accessory. | ||||||
|      */ |      */ | ||||||
|   | |||||||
| @@ -1,3 +1,7 @@ | |||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Interface to describe homebridge required elements. | ||||||
|  |  */ | ||||||
| export interface IAccessory { | export interface IAccessory { | ||||||
|     /** |     /** | ||||||
|      * Required by homebridge. |      * Required by homebridge. | ||||||
|   | |||||||
| @@ -1,382 +0,0 @@ | |||||||
| import { Activity } from "../Models/Activity"; |  | ||||||
| import { DeviceSetupItem } from "../Models/DeviceSetupItem"; |  | ||||||
| import { threadId } from "worker_threads"; |  | ||||||
| import { Input, Matrix, Output } from "../Models/Matrix"; |  | ||||||
|  |  | ||||||
| let Characteristic: HAPNodeJS.Characteristic; |  | ||||||
|  |  | ||||||
| const Harmony = require("harmony-websocket"); |  | ||||||
| interface IDevice { |  | ||||||
|     id: string, |  | ||||||
|     name: string, |  | ||||||
|     supportsCommand(commandName: string): boolean, |  | ||||||
|     getCommand(commandName: string): string, |  | ||||||
|     commands: { [name: string]: string }; |  | ||||||
|     on: boolean; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| interface IActivityState { |  | ||||||
|     currentActivity: Activity |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export enum RemoteKey { |  | ||||||
|     REWIND = 0, |  | ||||||
|     FAST_FORWARD = 1, |  | ||||||
|     NEXT_TRACK = 2, |  | ||||||
|     PREVIOUS_TRACK = 3, |  | ||||||
|     ARROW_UP = 4, |  | ||||||
|     ARROW_DOWN = 5, |  | ||||||
|     ARROW_LEFT = 6, |  | ||||||
|     ARROW_RIGHT = 7, |  | ||||||
|     SELECT = 8, |  | ||||||
|     BACK = 9, |  | ||||||
|     EXIT = 10, |  | ||||||
|     PLAY_PAUSE = 11, |  | ||||||
|     INFORMATION = 15, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| interface IHarmonyDataProviderProps { |  | ||||||
|     hubAddress: string, |  | ||||||
|     log: any, |  | ||||||
|     matrix: Matrix |  | ||||||
| } |  | ||||||
|  |  | ||||||
| class HarmonyDataProvider { |  | ||||||
|     private harmony: any; |  | ||||||
|     private log: any; |  | ||||||
|     private hubAddress: string = ""; |  | ||||||
|     private connected: boolean = false; |  | ||||||
|  |  | ||||||
|     private devices: { [name: string]: IDevice; } = {}; |  | ||||||
|     private states: { [controlUnitName: string]: (IActivityState | undefined) } = {}; |  | ||||||
|  |  | ||||||
|     private matrix: Matrix; |  | ||||||
|  |  | ||||||
|     constructor(props: IHarmonyDataProviderProps) { |  | ||||||
|         this.log = props.log; |  | ||||||
|         this.hubAddress = props.hubAddress; |  | ||||||
|         this.matrix = props.matrix; |  | ||||||
|  |  | ||||||
|         this.harmony = new Harmony(); |  | ||||||
|  |  | ||||||
|         //Listeners |  | ||||||
|         this.harmony.on('open', () => { |  | ||||||
|             this.connected = true; |  | ||||||
|         }); |  | ||||||
|         this.harmony.on('close', () => { |  | ||||||
|             this.connected = false; |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         this.connect(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     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); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     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); |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|         //Resolve device conflicts with other controlUnits |  | ||||||
|         devicesToTurnOff = this.sanitizeDeviceList(devicesToTurnOff, controlUnitName); |  | ||||||
|  |  | ||||||
|         //Turn off devices |  | ||||||
|         devicesToTurnOff.forEach((device: IDevice) => { |  | ||||||
|             this.powerOffDevice(device); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         this.states[controlUnitName] = undefined; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     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); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         //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 && this.devices[device.name]) { |  | ||||||
|                 if (!device.on) { |  | ||||||
|                     this.log(`Turning on device ${device.name}`) |  | ||||||
|                     await this.powerOnDevice(device); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         })); |  | ||||||
|  |  | ||||||
|         //Assign correct input |  | ||||||
|         await Promise.all( |  | ||||||
|             activity.deviceSetupItems.map(async (value: DeviceSetupItem) => { |  | ||||||
|                 let device: IDevice = this.getDeviceFromName(value.deviceName); |  | ||||||
|  |  | ||||||
|                 if (device && device.supportsCommand(`Input${value.input}`)) { |  | ||||||
|                     let command: string = device.getCommand(`Input${value.input}`); |  | ||||||
|                     await this.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); |  | ||||||
|  |  | ||||||
|             //Route hdmi |  | ||||||
|             if (matrixDevice.supportsCommand(inputCommandName) && matrixDevice.supportsCommand(outputCommandName)) { |  | ||||||
|                 await this.sendCommand(matrixDevice.getCommand(inputCommandName)); |  | ||||||
|                 await this.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); |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             //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 this.powerOffDevice(device); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 }) |  | ||||||
|             ); |  | ||||||
|  |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         //Assign current activity |  | ||||||
|         this.states[controlUnitName] = { currentActivity: activity }; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public volumeUp = async (controlUnitName: string) => { |  | ||||||
|         let volumeUpCommand: string = "Volume Up" |  | ||||||
|         if (this.states[controlUnitName]) { |  | ||||||
|             let volumeDevice: IDevice = this.getDeviceFromName(this.states[controlUnitName]!.currentActivity.volumeDeviceId); |  | ||||||
|             if (volumeDevice.supportsCommand(volumeUpCommand)) { |  | ||||||
|                 this.sendCommand(volumeDevice.getCommand(volumeUpCommand)); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public volumeDown = async (controlUnitName: string) => { |  | ||||||
|         let volumeDownCommand: string = "Volume Down" |  | ||||||
|         if (this.states[controlUnitName]) { |  | ||||||
|             let volumeDevice: IDevice = this.getDeviceFromName(this.states[controlUnitName]!.currentActivity.volumeDeviceId); |  | ||||||
|             if (volumeDevice.supportsCommand(volumeDownCommand)) { |  | ||||||
|                 this.sendCommand(volumeDevice.getCommand(volumeDownCommand)); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public sendKeyPress = async (controlUnitName: string, key: any) => { |  | ||||||
|         if (this.states[controlUnitName]) { |  | ||||||
|             let commandName: string = ""; |  | ||||||
|             let device: IDevice = this.getDeviceFromName(this.states[controlUnitName]!.currentActivity.controlDeviceId); |  | ||||||
|             switch (key) { |  | ||||||
|                 //@ts-ignore |  | ||||||
|                 case RemoteKey.ARROW_UP: { |  | ||||||
|                     commandName = "Direction Up"; |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|                 //@ts-ignore |  | ||||||
|                 case RemoteKey.ARROW_DOWN: { |  | ||||||
|                     commandName = "Direction Down"; |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|                 //@ts-ignore |  | ||||||
|                 case RemoteKey.ARROW_LEFT: { |  | ||||||
|                     commandName = "Direction Left"; |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|                 //@ts-ignore |  | ||||||
|                 case RemoteKey.ARROW_RIGHT: { |  | ||||||
|                     commandName = "Direction Right"; |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|                 //@ts-ignore |  | ||||||
|                 case RemoteKey.SELECT: { |  | ||||||
|                     commandName = "Select"; |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|                 //@ts-ignore |  | ||||||
|                 case RemoteKey.PLAY_PAUSE: { |  | ||||||
|                     commandName = "Pause"; |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|                 //@ts-ignore |  | ||||||
|                 case RemoteKey.INFORMATION: { |  | ||||||
|                     commandName = "Menu"; |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|                 //@ts-ignore |  | ||||||
|                 case RemoteKey.BACK: { |  | ||||||
|                     commandName = "Back"; |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|                 //@ts-ignore |  | ||||||
|                 case RemoteKey.EXIT: { |  | ||||||
|                     commandName = "Back"; |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             if (device && device.supportsCommand(commandName)) { |  | ||||||
|                 this.sendCommand(device.getCommand(commandName)); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public getIsActive(controlUnitName: string): Activity | undefined { |  | ||||||
|         return this.states[controlUnitName] ? this.states[controlUnitName]!.currentActivity : undefined; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Connect to harmony and receive device info |  | ||||||
|      */ |  | ||||||
|     private connect = async () => { |  | ||||||
|         await this.harmony.connect(this.hubAddress); |  | ||||||
|         let self = this; |  | ||||||
|  |  | ||||||
|         setTimeout(async function () { |  | ||||||
|             if (self.connected) { |  | ||||||
|                 let devices: any = await self.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 self.harmony.getDeviceCommands(dev.id); |  | ||||||
|                             deviceCommands.forEach((command: any) => { |  | ||||||
|                                 commands[command.label] = command.action; |  | ||||||
|                             }); |  | ||||||
|                             self.devices[dev.label] = { |  | ||||||
|                                 id: dev.id, |  | ||||||
|                                 name: dev.label, |  | ||||||
|                                 commands: commands, |  | ||||||
|                                 on: false, |  | ||||||
|                                 //Define device methods |  | ||||||
|                                 supportsCommand(commandName: string): boolean { |  | ||||||
|                                     let command = commands[commandName]; |  | ||||||
|                                     return (command) ? true : false; |  | ||||||
|                                 }, |  | ||||||
|                                 getCommand(commandName: string): string { |  | ||||||
|                                     return commands[commandName]; |  | ||||||
|                                 } |  | ||||||
|                             } |  | ||||||
|                         })); |  | ||||||
|                     self.log(`Harmony data provider ready`); |  | ||||||
|  |  | ||||||
|                 } catch (err) { |  | ||||||
|                     self.log(`ERROR - error connecting to harmony: ${err}`); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         }, 1000); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private powerOffDevice = async (device: IDevice) => { |  | ||||||
|         let powerOffCommand: string = "Power Off"; |  | ||||||
|         let powerToggleCommand: string = "Power Toggle"; |  | ||||||
|         if (device && device.supportsCommand(powerOffCommand)) { |  | ||||||
|             await this.sendCommand(device.getCommand(powerOffCommand)); |  | ||||||
|             device.on = false; |  | ||||||
|         } else if (device && device.supportsCommand(powerToggleCommand)) { |  | ||||||
|             await this.sendCommand(device.getCommand(powerToggleCommand)); |  | ||||||
|             device.on = false; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private powerOnDevice = async (device: IDevice) => { |  | ||||||
|         let powerOnCommand: string = "Power On"; |  | ||||||
|         let powerToggleCommand: string = "Power Toggle"; |  | ||||||
|         if (device && device.supportsCommand(powerOnCommand)) { |  | ||||||
|             await this.sendCommand(device.getCommand(powerOnCommand)); |  | ||||||
|             device.on = true; |  | ||||||
|         } else if (device && device.supportsCommand(powerToggleCommand)) { |  | ||||||
|             await this.sendCommand(device.getCommand(powerToggleCommand)); |  | ||||||
|             device.on = true; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private getDeviceFromName(deviceName: string): IDevice { |  | ||||||
|         return this.devices[deviceName]; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     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; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private sendCommand = async (command: string) => { |  | ||||||
|         try { |  | ||||||
|             let response = await this.harmony.sendCommand(JSON.stringify(command)); |  | ||||||
|             // this.log(`Sent command: ${JSON.stringify(command)} response: ${JSON.stringify(response)}`); |  | ||||||
|         } catch (err) { |  | ||||||
|             this.log(`ERROR - error sending command to harmony: ${err}`); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| 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; | ||||||
|  | } | ||||||
| @@ -1,15 +1,22 @@ | |||||||
|  | import { hub } from ".."; | ||||||
|  |  | ||||||
| export interface IDeviceSetupItemProps { | export interface IDeviceSetupItemProps { | ||||||
|     deviceName: string, |     deviceName: string, | ||||||
|     input: string |     input: string, | ||||||
|  |     hub: hub | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Data model to hold device setup items. | ||||||
|  |  */ | ||||||
| export class DeviceSetupItem { | export class DeviceSetupItem { | ||||||
|     private _deviceId: string = ""; |     private _deviceId: string = ""; | ||||||
|     private _input: string = ""; |     private _input: string = ""; | ||||||
|  |     private _hubType: hub; | ||||||
|     constructor(props: IDeviceSetupItemProps) { |     constructor(props: IDeviceSetupItemProps) { | ||||||
|         this._deviceId = props.deviceName; |         this._deviceId = props.deviceName; | ||||||
|         this._input = props.input; |         this._input = props.input; | ||||||
|  |         this._hubType = props.hub; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public get deviceName() { |     public get deviceName() { | ||||||
| @@ -19,4 +26,8 @@ export class DeviceSetupItem { | |||||||
|     public get input() { |     public get input() { | ||||||
|         return this._input; |         return this._input; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public get hubType() { | ||||||
|  |         return this._hubType; | ||||||
|  |     } | ||||||
| } | } | ||||||
| @@ -1,7 +1,10 @@ | |||||||
|  | import { hub } from ".."; | ||||||
|  |  | ||||||
| export interface IMatrixProps { | export interface IMatrixProps { | ||||||
|     inputs: Array<Input>, |     inputs: Array<Input>, | ||||||
|     outputs: Array<Output>, |     outputs: Array<Output>, | ||||||
|     deviceName: string, |     deviceName: string, | ||||||
|  |     hubType: hub | ||||||
| } | } | ||||||
|  |  | ||||||
| export interface Input { | export interface Input { | ||||||
| @@ -14,15 +17,20 @@ export interface Output { | |||||||
|     outputDevice: string, |     outputDevice: string, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Data model to hold matrix information. | ||||||
|  |  */ | ||||||
| export class Matrix { | export class Matrix { | ||||||
|     private _inputs: Array<Input> = []; |     private _inputs: Array<Input> = []; | ||||||
|     private _outputs: Array<Output> = []; |     private _outputs: Array<Output> = []; | ||||||
|     private _deviceName: string; |     private _deviceName: string; | ||||||
|  |     private _hubType: hub = "Harmony"; | ||||||
|  |  | ||||||
|     constructor(props: IMatrixProps) { |     constructor(props: IMatrixProps) { | ||||||
|         this._inputs = props.inputs; |         this._inputs = props.inputs; | ||||||
|         this._outputs = props.outputs; |         this._outputs = props.outputs; | ||||||
|         this._deviceName = props.deviceName; |         this._deviceName = props.deviceName; | ||||||
|  |         this._hubType = props.hubType; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public get inputs(): Array<Input> { |     public get inputs(): Array<Input> { | ||||||
| @@ -36,4 +44,8 @@ export class Matrix { | |||||||
|     public get deviceName(): string { |     public get deviceName(): string { | ||||||
|         return this._deviceName; |         return this._deviceName; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public get hubType(): hub { | ||||||
|  |         return this._hubType; | ||||||
|  |     } | ||||||
| } | } | ||||||
| @@ -1,5 +1,8 @@ | |||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Helper function to convert callbacks into promises | ||||||
|  |  * @param func  | ||||||
|  |  */ | ||||||
| export default function callbackify(func: (...args: any[]) => Promise<any>): Function { | export default function callbackify(func: (...args: any[]) => Promise<any>): Function { | ||||||
|     return (...args: any[]) => { |     return (...args: any[]) => { | ||||||
|         const onlyArgs: any[] = []; |         const onlyArgs: any[] = []; | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								src/Util/Sleep.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/Util/Sleep.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | export function sleep(ms: number) { | ||||||
|  |     return new Promise(resolve => setTimeout(resolve, ms)); | ||||||
|  | } | ||||||
							
								
								
									
										53
									
								
								src/index.ts
									
									
									
									
									
								
							
							
						
						
									
										53
									
								
								src/index.ts
									
									
									
									
									
								
							| @@ -2,11 +2,17 @@ import { ControlUnit } from "./Accessories/ControlUnit"; | |||||||
| import { Activity } from "./Models/Activity"; | import { Activity } from "./Models/Activity"; | ||||||
| import { DeviceSetupItem } from "./Models/DeviceSetupItem"; | import { DeviceSetupItem } from "./Models/DeviceSetupItem"; | ||||||
| import { Input, Output, Matrix } from "./Models/Matrix"; | import { Input, Output, Matrix } from "./Models/Matrix"; | ||||||
| import HarmonyDataProvider from "./DataProviders/HarmonyDataProvider"; | import HubDataProvider from './DataProviders/HubDataProvider'; | ||||||
|  |  | ||||||
| let Accessory: any; | let Accessory: any; | ||||||
| let Homebridge: any; | let Homebridge: any; | ||||||
|  |  | ||||||
|  | export type hub = "Harmony" | "Neeo"; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Main entry. | ||||||
|  |  * @param homebridge  | ||||||
|  |  */ | ||||||
| export default function (homebridge: any) { | export default function (homebridge: any) { | ||||||
|     Homebridge = homebridge; |     Homebridge = homebridge; | ||||||
|     Accessory = homebridge.platformAccessory; |     Accessory = homebridge.platformAccessory; | ||||||
| @@ -23,6 +29,7 @@ class HarmonyMatrixPlatform { | |||||||
|     config: any = {}; |     config: any = {}; | ||||||
|     api: any; |     api: any; | ||||||
|     externalAccessories: Array<any> = []; |     externalAccessories: Array<any> = []; | ||||||
|  |     dataProvider: HubDataProvider | null; | ||||||
|  |  | ||||||
|     constructor(log: any, config: any, api: any) { |     constructor(log: any, config: any, api: any) { | ||||||
|         this.log = log; |         this.log = log; | ||||||
| @@ -30,11 +37,19 @@ class HarmonyMatrixPlatform { | |||||||
|         this.api = api; |         this.api = api; | ||||||
|         this.log('INFO - Registering Harmony Matrix Platform'); |         this.log('INFO - Registering Harmony Matrix Platform'); | ||||||
|         this.api.on('didFinishLaunching', this.didFinishLaunching.bind(this)); |         this.api.on('didFinishLaunching', this.didFinishLaunching.bind(this)); | ||||||
|  |  | ||||||
|  |         this.dataProvider = null; | ||||||
|  |         this.log("This is new"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Handler for didFinishLaunching | ||||||
|  |      * Happens after constructor | ||||||
|  |      */ | ||||||
|     didFinishLaunching() { |     didFinishLaunching() { | ||||||
|         this.log(`Publishing external accessories`); |         this.log(`Publishing external accessories`); | ||||||
|  |  | ||||||
|  |         //This is required in order to have multiple tv remotes on one platform | ||||||
|         this.externalAccessories.forEach((accessory: ControlUnit) => { |         this.externalAccessories.forEach((accessory: ControlUnit) => { | ||||||
|             this.api.publishExternalAccessories("HarmonyMatrixPlatform", [accessory.platformAccessory]); |             this.api.publishExternalAccessories("HarmonyMatrixPlatform", [accessory.platformAccessory]); | ||||||
|         }) |         }) | ||||||
| @@ -47,12 +62,14 @@ class HarmonyMatrixPlatform { | |||||||
|      */ |      */ | ||||||
|     accessories(callback: (accessories: Array<ControlUnit>) => void) { |     accessories(callback: (accessories: Array<ControlUnit>) => void) { | ||||||
|         //Parse ip |         //Parse ip | ||||||
|         let hubIp: string = this.config["hubIp"]; |         let harmonyIp: string = this.config["harmonyHubIp"]; | ||||||
|  |         let neeoIp: string = this.config["neeoHubIp"]; | ||||||
|  |  | ||||||
|         //Parse matrix |         //Parse matrix | ||||||
|         let configInputs: any = this.config["Matrix"]["Inputs"]; |         let configInputs: any = this.config["Matrix"]["Inputs"]; | ||||||
|         let configOutputs: any = this.config["Matrix"]["Outputs"]; |         let configOutputs: any = this.config["Matrix"]["Outputs"]; | ||||||
|         let matrixName: string = this.config["Matrix"]["DeviceName"]; |         let matrixName: string = this.config["Matrix"]["DeviceName"]; | ||||||
|  |         let hub: hub = this.config["Matrix"]["Hub"]; | ||||||
|  |  | ||||||
|         let inputs: Array<Input> = []; |         let inputs: Array<Input> = []; | ||||||
|         let outputs: Array<Output> = []; |         let outputs: Array<Output> = []; | ||||||
| @@ -85,11 +102,13 @@ class HarmonyMatrixPlatform { | |||||||
|             inputs: inputs, |             inputs: inputs, | ||||||
|             outputs: outputs, |             outputs: outputs, | ||||||
|             deviceName: matrixName, |             deviceName: matrixName, | ||||||
|  |             hubType: hub | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         //construct data provider |         //construct data provider | ||||||
|         let dataProvider = new HarmonyDataProvider({ |         this.dataProvider = new HubDataProvider({ | ||||||
|             hubAddress: hubIp, |             harmonyHubAddress: harmonyIp, | ||||||
|  |             neeoHubAddress: neeoIp, | ||||||
|             matrix: matrix, |             matrix: matrix, | ||||||
|             log: this.log |             log: this.log | ||||||
|         }); |         }); | ||||||
| @@ -109,7 +128,8 @@ class HarmonyMatrixPlatform { | |||||||
|                     //Add device |                     //Add device | ||||||
|                     devices.push(new DeviceSetupItem({ |                     devices.push(new DeviceSetupItem({ | ||||||
|                         deviceName: configDevice["DeviceName"], |                         deviceName: configDevice["DeviceName"], | ||||||
|                         input: configDevice["Input"] |                         input: configDevice["Input"], | ||||||
|  |                         hub: configDevice["Hub"] | ||||||
|                     })); |                     })); | ||||||
|                     this.log(`INFO - Added device '${configDevice["DeviceName"]}' for activity '${configActivity["DisplayName"]}'`); |                     this.log(`INFO - Added device '${configDevice["DeviceName"]}' for activity '${configActivity["DisplayName"]}'`); | ||||||
|                 }); |                 }); | ||||||
| @@ -126,27 +146,38 @@ class HarmonyMatrixPlatform { | |||||||
|                 this.log(`INFO - Added activity '${configActivity["DisplayName"]}'`); |                 this.log(`INFO - Added activity '${configActivity["DisplayName"]}'`); | ||||||
|             }); |             }); | ||||||
|  |  | ||||||
|             // let accessory = new Accessory(configControlUnit["DisplayName"], |  | ||||||
|             //     Homebridge.hap.uuid.generate(configControlUnit["DisplayName"], Homebridge.hap.Accessory.Categories.TELEVISION)); |  | ||||||
|  |  | ||||||
|             let controlUnit: ControlUnit = new ControlUnit({ |             let controlUnit: ControlUnit = new ControlUnit({ | ||||||
|                 dataProvider: dataProvider, |                 dataProvider: this.dataProvider!, | ||||||
|                 displayName: configControlUnit["DisplayName"], |                 displayName: configControlUnit["DisplayName"], | ||||||
|                 api: this.api, |                 api: this.api, | ||||||
|                 log: this.log, |                 log: this.log, | ||||||
|                 activities: activities, |                 activities: activities, | ||||||
|                 homebridge: Homebridge |                 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 |             //@ts-ignore | ||||||
|             let accessory = controlUnit as homebridge.platformAccessory; |             let accessory = controlUnit as homebridge.platformAccessory; | ||||||
|  |             //@ts-ignore | ||||||
|  |             let externalAccessory = controlUnitExternal as homebridge.platformAccessory; | ||||||
|  |  | ||||||
|             //Add control unit |             //Add control unit | ||||||
|             controlUnits.push(accessory); |             controlUnits.push(accessory); | ||||||
|  |             //Add to list of remotes | ||||||
|  |             this.externalAccessories.push(externalAccessory); | ||||||
|  |  | ||||||
|             this.log(`INFO - Added ControlUnit`); |             this.log(`INFO - Added ControlUnit`); | ||||||
|         }); |         }); | ||||||
|         this.externalAccessories = controlUnits; |  | ||||||
|         callback(controlUnits); |         callback(controlUnits); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -5,6 +5,7 @@ | |||||||
|         "declaration": true, |         "declaration": true, | ||||||
|         "outDir": "./bin", |         "outDir": "./bin", | ||||||
|         "strict": true, |         "strict": true, | ||||||
|  |         "sourceMap": true, | ||||||
|     }, |     }, | ||||||
|     "include": [ |     "include": [ | ||||||
|         "src" |         "src" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user