Compare commits
	
		
			4 Commits
		
	
	
		
			1.2.0
			...
			feature/ne
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | ed0b418b4d | ||
|  | 2475aea1c8 | ||
|  | 31c5c5b9e2 | ||
|  | a539287b9e | 
							
								
								
									
										1
									
								
								.bash_profile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.bash_profile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | source /nvm.sh | ||||||
							
								
								
									
										80
									
								
								.drone.yml
									
									
									
									
									
								
							
							
						
						
									
										80
									
								
								.drone.yml
									
									
									
									
									
								
							| @@ -1,80 +0,0 @@ | |||||||
| kind: pipeline |  | ||||||
| type: docker |  | ||||||
| name: default |  | ||||||
|  |  | ||||||
| clone: |  | ||||||
|   disable: true |  | ||||||
|  |  | ||||||
| steps: |  | ||||||
|   - name: clone |  | ||||||
|     image: alpine/git |  | ||||||
|     commands: |  | ||||||
|       - git clone https://gitea.watsonlabs.net/watsonb8/homebridge-harmony-control.git . |  | ||||||
|       - git checkout $DRONE_COMMIT |  | ||||||
|  |  | ||||||
|   - name: build |  | ||||||
|     image: node |  | ||||||
|     commands: |  | ||||||
|       - npm install |  | ||||||
|       - npm run build |  | ||||||
|  |  | ||||||
|   - name: version |  | ||||||
|     image: node |  | ||||||
|     commands: |  | ||||||
|       - export version=`node -p "require('./package.json').version"` |  | ||||||
|       - export commit=`echo $DRONE_COMMIT | cut -c1-5` |  | ||||||
|       - npm version prerelease --preid=$commit --git-tag-version=false --allow-same-version=true |  | ||||||
|     when: |  | ||||||
|       event: |  | ||||||
|         exclude: |  | ||||||
|           - tag |  | ||||||
|           - pull_request |  | ||||||
|       branch: |  | ||||||
|         include: |  | ||||||
|           - master |  | ||||||
|  |  | ||||||
|   - name: publish pre |  | ||||||
|     image: plugins/npm |  | ||||||
|     settings: |  | ||||||
|       username: |  | ||||||
|         from_secret: npm_username |  | ||||||
|       password: |  | ||||||
|         from_secret: npm_password |  | ||||||
|       email: brandon@watsonlabs.net |  | ||||||
|       registry: "http://10.44.1.6:4873/" |  | ||||||
|     when: |  | ||||||
|       event: |  | ||||||
|         exclude: |  | ||||||
|           - tag |  | ||||||
|           - pull_request |  | ||||||
|       branch: |  | ||||||
|         include: |  | ||||||
|           - master |  | ||||||
|  |  | ||||||
|   - name: publish tagged version |  | ||||||
|     image: plugins/npm |  | ||||||
|     settings: |  | ||||||
|       username: |  | ||||||
|         from_secret: npm_username |  | ||||||
|       password: |  | ||||||
|         from_secret: npm_password |  | ||||||
|       email: brandon@watsonlabs.net |  | ||||||
|       registry: "http://10.44.1.6:4873/" |  | ||||||
|     when: |  | ||||||
|       event: |  | ||||||
|         - tag |  | ||||||
|       exclude: |  | ||||||
|         - pull_request |  | ||||||
|  |  | ||||||
|   - name: Notify |  | ||||||
|     image: drillster/drone-email |  | ||||||
|     settings: |  | ||||||
|       host: 10.44.1.13 |  | ||||||
|       username: srvGitea |  | ||||||
|       password: |  | ||||||
|         from_secret: smtp_password |  | ||||||
|       from: drone@watsonlabs.net |  | ||||||
|       skip_verify: true |  | ||||||
|     when: |  | ||||||
|       status: |  | ||||||
|         - failure |  | ||||||
							
								
								
									
										33
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										33
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							| @@ -1,19 +1,16 @@ | |||||||
| { | { | ||||||
|   // Use IntelliSense to learn about possible attributes. |     // Use IntelliSense to learn about possible attributes. | ||||||
|   // Hover to view descriptions of existing attributes. |     // Hover to view descriptions of existing attributes. | ||||||
|   // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 |     // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||||||
|   "version": "0.2.0", |     "version": "0.2.0", | ||||||
|   "configurations": [ |     "configurations": [ | ||||||
|     { |         { | ||||||
|       "type": "node", |             "type": "node", | ||||||
|       "request": "launch", |             "request": "launch", | ||||||
|       "name": "Launch Program", |             "name": "Launch Program", | ||||||
|       "preLaunchTask": "build", |             "preLaunchTask": "build and install", | ||||||
|       "program": "/Users/brandonwatson/.nvm/versions/node/v14.15.0/lib/node_modules/homebridge/bin/homebridge", |             "program": "/Users/brandonwatson/.npm-global/bin/homebridge", | ||||||
|       "env": { |             "sourceMaps": true, | ||||||
|         "HOMEBRIDGE_OPTS": "/Users/brandonwatson/.homebridge" |         } | ||||||
|       }, |     ] | ||||||
|       "sourceMaps": true | } | ||||||
|     } |  | ||||||
|   ] |  | ||||||
| } |  | ||||||
							
								
								
									
										33
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										33
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							| @@ -1,18 +1,17 @@ | |||||||
| { | { | ||||||
|   // See https://go.microsoft.com/fwlink/?LinkId=733558 |     // See https://go.microsoft.com/fwlink/?LinkId=733558  | ||||||
|   // for the documentation about the tasks.json format |     // for the documentation about the tasks.json format | ||||||
|   "version": "2.0.0", |     "version": "2.0.0", | ||||||
|   "tasks": [ |     "tasks": [ | ||||||
|     { |         { | ||||||
|       "type": "npm", |             "type": "npm", | ||||||
|       "script": "build", |             "script": "build", | ||||||
|       "label": "build", |             "problemMatcher": [] | ||||||
|       "problemMatcher": [] |         }, | ||||||
|     }, |         { | ||||||
|     { |             "type": "shell", | ||||||
|       "type": "shell", |             "label": "build and install", | ||||||
|       "label": "build and install", |             "command": "npm run build&&sudo npm install -g --unsafe-perm ." | ||||||
|       "command": "npm run build&&sudo npm install -g --unsafe-perm ." |         } | ||||||
|     } |     ] | ||||||
|   ] | } | ||||||
| } |  | ||||||
							
								
								
									
										178
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										178
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,178 +0,0 @@ | |||||||
| # homebridge-harmony-control |  | ||||||
|  |  | ||||||
| A convenient way to control your home through homekit utilizing one or many Logitech Harmony remotes. |  | ||||||
|  |  | ||||||
| ## Description |  | ||||||
|  |  | ||||||
| This plugin not only allows for control of your one or many entertainment consoles, but it also supports use of a more complicated matrix switch if applicable. In addition, this plugin can expose any entertainment devices directly to homekit via stateless or stateful switch. |  | ||||||
|  |  | ||||||
| ## Installation |  | ||||||
|  |  | ||||||
| 1. Clone the repository by running `git clone ssh://git@thebword.ddns.net:3122/watsonb8/homebridge-harmony-control.git` |  | ||||||
| 2. Run `npm install` to install required modules |  | ||||||
| 3. Run `npm run build` to build the module |  | ||||||
| 4. Run `npm link` or install globally to link this instance to your global homebridge instance |  | ||||||
|  |  | ||||||
| ## Configuration |  | ||||||
|  |  | ||||||
| ``` |  | ||||||
| { |  | ||||||
|       "platform": "HarmonyHubMatrix", |  | ||||||
|       "EmitDevicesOnStartup": false, |  | ||||||
|       "Hubs": [ |  | ||||||
|         { |  | ||||||
|           "Name": "LivingRoomHub", |  | ||||||
|           "Ip": "exampleOne.com" |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|           "Name": "RackHub", |  | ||||||
|           "Ip": "exampleTwo.com" |  | ||||||
|         } |  | ||||||
|       ], |  | ||||||
|       "Devices": [ |  | ||||||
|         { |  | ||||||
|           "Name": "LG TV", |  | ||||||
|           "Hub": "LivingRoomHub" |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|           "Name": "JVC AV Receiver", |  | ||||||
|           "Hub": "LivingRoomHub" |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|           "Name": "Microsoft Xbox One", |  | ||||||
|           "Hub": "RackHub" |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|           "Name": "Apple TV Gen 4", |  | ||||||
|           "Hub": "RackHub" |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|           "Name": "Gefen AV Switch", |  | ||||||
|           "Hub": "RackHub" |  | ||||||
|         } |  | ||||||
|       ], |  | ||||||
|       "Matrix": { |  | ||||||
|         "DeviceName": "Gefen AV Switch", |  | ||||||
|         "Inputs": [ |  | ||||||
|           { |  | ||||||
|             "InputNumber": "1", |  | ||||||
|             "InputDevice": "Microsoft Xbox One" |  | ||||||
|           }, |  | ||||||
|           { |  | ||||||
|             "InputNumber": "5", |  | ||||||
|             "InputDevice": "Apple TV Gen 4" |  | ||||||
|           }, |  | ||||||
|         ], |  | ||||||
|         "Outputs": [ |  | ||||||
|           { |  | ||||||
|             "OutputLetter": "B", |  | ||||||
|             "OutputDevice": "LG TV" |  | ||||||
|           }, |  | ||||||
|         ] |  | ||||||
|       }, |  | ||||||
|       "ControlUnits": [ |  | ||||||
|         { |  | ||||||
|           "DisplayName": "Living Room", |  | ||||||
|           "Activities": [ |  | ||||||
|             { |  | ||||||
|               "DisplayName": "Xbox One", |  | ||||||
|               "DeviceSetupList": [ |  | ||||||
|                 { |  | ||||||
|                   "DeviceName": "Microsoft Xbox One", |  | ||||||
|                   "Input": [] |  | ||||||
|                 }, |  | ||||||
|                 { |  | ||||||
|                   "DeviceName": "LG TV", |  | ||||||
|                   "Input": "Hdmi1" |  | ||||||
|                 }, |  | ||||||
|                 { |  | ||||||
|                   "DeviceName": "JVC AV Receiver", |  | ||||||
|                   "Input": "Dvd" |  | ||||||
|                 } |  | ||||||
|               ], |  | ||||||
|               "VolumeDevice": "JVC AV Receiver", |  | ||||||
|               "ControlDevice": "Microsoft Xbox One", |  | ||||||
|               "OutputDevice": "LG TV", |  | ||||||
|               "UseMatrix": true |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|               "DisplayName": "Apple TV", |  | ||||||
|               "DeviceSetupList": [ |  | ||||||
|                 { |  | ||||||
|                   "DeviceName": "Apple TV Gen 4", |  | ||||||
|                   "Input": [] |  | ||||||
|                 }, |  | ||||||
|                 { |  | ||||||
|                   "DeviceName": "LG TV", |  | ||||||
|                   "Input": "Hdmi1" |  | ||||||
|                 }, |  | ||||||
|                 { |  | ||||||
|                   "DeviceName": "JVC AV Receiver", |  | ||||||
|                   "Input": "Dvd" |  | ||||||
|                 } |  | ||||||
|               ], |  | ||||||
|               "VolumeDevice": "JVC AV Receiver", |  | ||||||
|               "ControlDevice": "Apple TV Gen 4", |  | ||||||
|               "OutputDevice": "LG TV", |  | ||||||
|               "UseMatrix": true |  | ||||||
|             }, |  | ||||||
|           ] |  | ||||||
|         }, |  | ||||||
|       ], |  | ||||||
|       "DeviceButtons": [ |  | ||||||
|         { |  | ||||||
|           "DeviceName": "JVC AV Receiver", |  | ||||||
|           "ButtonName": "SurroundToggle", |  | ||||||
|           "DisplayName": "Surround Toggle", |  | ||||||
|           "IsStateful": true |  | ||||||
|         }, |  | ||||||
|       ] |  | ||||||
|     } |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| #### Platform |  | ||||||
|  |  | ||||||
| - `EmitDevicesOnStartup`: If true, each device frome each hub will be written to the console on startup |  | ||||||
|  |  | ||||||
| - `Hubs`: A list of harmony hub configurations |  | ||||||
|  |  | ||||||
| - `Devices`: A list of devices to be used and corresponding hubs |  | ||||||
|  |  | ||||||
| - `Matrix`: An optional configuration for an hdmi matrix |  | ||||||
|  |  | ||||||
| - `ControlUnits`: Configures one logical control unit. This is usually a single TV in a single room. |  | ||||||
|  |  | ||||||
| #### Devices |  | ||||||
|  |  | ||||||
| - `Name`: The name of the device to use from an existing harmony hub configuration |  | ||||||
|  |  | ||||||
| > Note: Use `EmitDevicesOnStartup` if you are unsure of which devices are available. |  | ||||||
|  |  | ||||||
| - `Hub`: The name of the hub to look the device on |  | ||||||
|  |  | ||||||
| #### Matrix |  | ||||||
|  |  | ||||||
| - `DeviceName`: The name of the matrix device as seen in the harmony configuration |  | ||||||
|  |  | ||||||
| > Note: This device must appear in the device list |  | ||||||
|  |  | ||||||
| - `Inputs`: A list of input devices and their corresponding input number as it relates to the matrix switch |  | ||||||
|  |  | ||||||
| - `Outputs`: A list of output devices and their corresponding output letter as it relates to the matrix switch |  | ||||||
|  |  | ||||||
| #### ControlUnits |  | ||||||
|  |  | ||||||
| This is the logical display unit. It will appear as a tv accessory in the home app. |  | ||||||
|  |  | ||||||
| - `DisplayName`: The name of the control unit |  | ||||||
|  |  | ||||||
| - `Activities`: A list of activity configurations. These will be the inputs associated with the tv accessory |  | ||||||
| - `DeviceSetupList`: A list of devices associated with an activity and their corresponding input setting |  | ||||||
|  |  | ||||||
| - `VolumeDevice`: The device that should be used to control the volume of the activity. This is usually a stereo or a tv |  | ||||||
|  |  | ||||||
| - `ControlDevice`: The device that is used to control UI navigation |  | ||||||
|  |  | ||||||
| - `OutputDevice`: The device that is used to display the content. This is usually a TV |  | ||||||
|  |  | ||||||
| - `UseMatrix`: If true, corresponding commands will be issued to the matrix device to route input to output |  | ||||||
							
								
								
									
										336
									
								
								config.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										336
									
								
								config.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,336 @@ | |||||||
|  | { | ||||||
|  |     "bridge": { | ||||||
|  |         "name": "Homebridge", | ||||||
|  |         "username": "CC:22:3D:E3:CE:42", | ||||||
|  |         "port": 51826, | ||||||
|  |         "pin": "031-45-154" | ||||||
|  |     }, | ||||||
|  |     "platforms": [ | ||||||
|  |         { | ||||||
|  |             "platform": "websocket", | ||||||
|  |             "name": "websocket", | ||||||
|  |             "port": 4050 | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             "platform": "HarmonyHubMatrix", | ||||||
|  |             "harmonyHubIp": "192.168.1.14", | ||||||
|  |             "neeoHubIp": "192.168.1.24", | ||||||
|  |             "Matrix": { | ||||||
|  |                 "Hub": "Harmony", | ||||||
|  |                 "DeviceName": "Gefen AV Switch", | ||||||
|  |                 "Inputs": [ | ||||||
|  |                     { | ||||||
|  |                         "InputNumber": "1", | ||||||
|  |                         "InputDevice": "Microsoft Xbox One" | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         "InputNumber": "5", | ||||||
|  |                         "InputDevice": "Apple TV Gen 4" | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         "InputNumber": "3", | ||||||
|  |                         "InputDevice": "Chromecast" | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         "InputNumber": "2", | ||||||
|  |                         "InputDevice": "Sony PS4" | ||||||
|  |                     } | ||||||
|  |                 ], | ||||||
|  |                 "Outputs": [ | ||||||
|  |                     { | ||||||
|  |                         "OutputLetter": "B", | ||||||
|  |                         "OutputDevice": "LG TV" | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         "OutputLetter": "C", | ||||||
|  |                         "OutputDevice": "Vizio TV" | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         "OutputLetter": "D", | ||||||
|  |                         "OutputDevice": "Westinghouse TV" | ||||||
|  |                     } | ||||||
|  |                 ] | ||||||
|  |             }, | ||||||
|  |             "ControlUnits": [ | ||||||
|  |                 { | ||||||
|  |                     "DisplayName": "Living Room", | ||||||
|  |                     "Activities": [ | ||||||
|  |                         { | ||||||
|  |                             "DisplayName": "Chromecast", | ||||||
|  |                             "DeviceSetupList": [ | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|  |                                     "DeviceName": "LG TV", | ||||||
|  |                                     "Input": "HDMI1" | ||||||
|  |                                 }, | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|  |                                     "DeviceName": "JVC AV Receiver", | ||||||
|  |                                     "Input": "DVD" | ||||||
|  |                                 }, | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|  |                                     "DeviceName": "Chromecast", | ||||||
|  |                                     "Input": [] | ||||||
|  |                                 } | ||||||
|  |                             ], | ||||||
|  |                             "VolumeDevice": "JVC AV Receiver", | ||||||
|  |                             "ControlDevice": "Chromecast", | ||||||
|  |                             "OutputDevice": "LG TV", | ||||||
|  |                             "UseMatrix": "true" | ||||||
|  |                         }, | ||||||
|  |                         { | ||||||
|  |                             "DisplayName": "Play Xbox One", | ||||||
|  |                             "DeviceSetupList": [ | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|  |                                     "DeviceName": "Microsoft Xbox One", | ||||||
|  |                                     "Input": [] | ||||||
|  |                                 }, | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|  |                                     "DeviceName": "LG TV", | ||||||
|  |                                     "Input": "HDMI1" | ||||||
|  |                                 }, | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|  |                                     "DeviceName": "JVC AV Receiver", | ||||||
|  |                                     "Input": "DVD" | ||||||
|  |                                 } | ||||||
|  |                             ], | ||||||
|  |                             "VolumeDevice": "JVC AV Receiver", | ||||||
|  |                             "ControlDevice": "Microsoft Xbox One", | ||||||
|  |                             "OutputDevice": "LG TV", | ||||||
|  |                             "UseMatrix": "true" | ||||||
|  |                         }, | ||||||
|  |                         { | ||||||
|  |                             "DisplayName": "Play PS4", | ||||||
|  |                             "DeviceSetupList": [ | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|  |                                     "DeviceName": "Sony PS4", | ||||||
|  |                                     "Input": [] | ||||||
|  |                                 }, | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|  |                                     "DeviceName": "LG TV", | ||||||
|  |                                     "Input": "HDMI1" | ||||||
|  |                                 }, | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|  |                                     "DeviceName": "JVC AV Receiver", | ||||||
|  |                                     "Input": "DVD" | ||||||
|  |                                 } | ||||||
|  |                             ], | ||||||
|  |                             "VolumeDevice": "JVC AV Receiver", | ||||||
|  |                             "ControlDevice": "Sony PS4", | ||||||
|  |                             "OutputDevice": "LG TV", | ||||||
|  |                             "UseMatrix": "true" | ||||||
|  |                         }, | ||||||
|  |                         { | ||||||
|  |                             "DisplayName": "Apple TV", | ||||||
|  |                             "DeviceSetupList": [ | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|  |                                     "DeviceName": "Apple TV Gen 4", | ||||||
|  |                                     "Input": [] | ||||||
|  |                                 }, | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|  |                                     "DeviceName": "LG TV", | ||||||
|  |                                     "Input": "HDMI1" | ||||||
|  |                                 }, | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|  |                                     "DeviceName": "JVC AV Receiver", | ||||||
|  |                                     "Input": "DVD" | ||||||
|  |                                 } | ||||||
|  |                             ], | ||||||
|  |                             "VolumeDevice": "JVC AV Receiver", | ||||||
|  |                             "ControlDevice": "Apple TV Gen 4", | ||||||
|  |                             "OutputDevice": "LG TV", | ||||||
|  |                             "UseMatrix": "true" | ||||||
|  |                         }, | ||||||
|  |                         { | ||||||
|  |                             "DisplayName": "Listen to Music", | ||||||
|  |                             "DeviceSetupList": [ | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|  |                                     "DeviceName": "JVC AV Receiver", | ||||||
|  |                                     "Input": "DVD" | ||||||
|  |                                 } | ||||||
|  |                             ], | ||||||
|  |                             "VolumeDevice": "JVC AV Receiver", | ||||||
|  |                             "ControlDevice": [], | ||||||
|  |                             "OutputDevice": [], | ||||||
|  |                             "UseMatrix": "false" | ||||||
|  |                         } | ||||||
|  |                     ] | ||||||
|  |                 }, | ||||||
|  |                 { | ||||||
|  |                     "DisplayName": "Bedroom", | ||||||
|  |                     "Activities": [ | ||||||
|  |                         { | ||||||
|  |                             "DisplayName": "Chromecast", | ||||||
|  |                             "DeviceSetupList": [ | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Neeo", | ||||||
|  |                                     "DeviceName": "Vizio TV", | ||||||
|  |                                     "Input": "HDMI1" | ||||||
|  |                                 }, | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Neeo", | ||||||
|  |                                     "DeviceName": "Harmon Kardon Receicer", | ||||||
|  |                                     "Input": "Game" | ||||||
|  |                                 }, | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|  |                                     "DeviceName": "Chromecast", | ||||||
|  |                                     "Input": [] | ||||||
|  |                                 } | ||||||
|  |                             ], | ||||||
|  |                             "VolumeDevice": "Harmon Kardon Receicer", | ||||||
|  |                             "ControlDevice": "Chromecast", | ||||||
|  |                             "OutputDevice": "Vizio TV", | ||||||
|  |                             "UseMatrix": "true" | ||||||
|  |                         }, | ||||||
|  |                         { | ||||||
|  |                             "DisplayName": "Play Xbox One", | ||||||
|  |                             "DeviceSetupList": [ | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|  |                                     "DeviceName": "Microsoft Xbox One", | ||||||
|  |                                     "Input": [] | ||||||
|  |                                 }, | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Neeo", | ||||||
|  |                                     "DeviceName": "Vizio TV", | ||||||
|  |                                     "Input": "HDMI1" | ||||||
|  |                                 }, | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Neeo", | ||||||
|  |                                     "DeviceName": "Harmon Kardon Receicer", | ||||||
|  |                                     "Input": "Game" | ||||||
|  |                                 } | ||||||
|  |                             ], | ||||||
|  |                             "VolumeDevice": "Harmon Kardon Receicer", | ||||||
|  |                             "ControlDevice": "Microsoft Xbox One", | ||||||
|  |                             "OutputDevice": "Vizio TV", | ||||||
|  |                             "UseMatrix": "true" | ||||||
|  |                         }, | ||||||
|  |                         { | ||||||
|  |                             "DisplayName": "Play PS4", | ||||||
|  |                             "DeviceSetupList": [ | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|  |                                     "DeviceName": "Sony PS4", | ||||||
|  |                                     "Input": [] | ||||||
|  |                                 }, | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Neeo", | ||||||
|  |                                     "DeviceName": "Vizio TV", | ||||||
|  |                                     "Input": "HDMI1" | ||||||
|  |                                 }, | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Neeo", | ||||||
|  |                                     "DeviceName": "Harmon Kardon Receicer", | ||||||
|  |                                     "Input": "DVD" | ||||||
|  |                                 } | ||||||
|  |                             ], | ||||||
|  |                             "VolumeDevice": "Harmon Kardon Receicer", | ||||||
|  |                             "ControlDevice": "Sony PS4", | ||||||
|  |                             "OutputDevice": "LG TV", | ||||||
|  |                             "UseMatrix": "true" | ||||||
|  |                         }, | ||||||
|  |                         { | ||||||
|  |                             "DisplayName": "Apple TV", | ||||||
|  |                             "DeviceSetupList": [ | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|  |                                     "DeviceName": "Apple TV Gen 4", | ||||||
|  |                                     "Input": [] | ||||||
|  |                                 }, | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Neeo", | ||||||
|  |                                     "DeviceName": "Vizio TV", | ||||||
|  |                                     "Input": "HDMI1" | ||||||
|  |                                 }, | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Neeo", | ||||||
|  |                                     "DeviceName": "Harmon Kardon Receicer", | ||||||
|  |                                     "Input": "Game" | ||||||
|  |                                 } | ||||||
|  |                             ], | ||||||
|  |                             "VolumeDevice": "Harmon Kardon Receicer", | ||||||
|  |                             "ControlDevice": "Apple TV Gen 4", | ||||||
|  |                             "OutputDevice": "Vizio TV", | ||||||
|  |                             "UseMatrix": "true" | ||||||
|  |                         } | ||||||
|  |                     ] | ||||||
|  |                 }, | ||||||
|  |                 { | ||||||
|  |                     "DisplayName": "Kitchen", | ||||||
|  |                     "Activities": [ | ||||||
|  |                         { | ||||||
|  |                             "DisplayName": "Chromecast", | ||||||
|  |                             "DeviceSetupList": [ | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|  |                                     "DeviceName": "Chromecast", | ||||||
|  |                                     "Input": [] | ||||||
|  |                                 }, | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|  |                                     "DeviceName": "Westinghouse TV", | ||||||
|  |                                     "Input": "HDMI1" | ||||||
|  |                                 } | ||||||
|  |                             ], | ||||||
|  |                             "VolumeDevice": "Westinghouse TV", | ||||||
|  |                             "ControlDevice": "Chromecast", | ||||||
|  |                             "OutputDevice": "Westinghouse TV", | ||||||
|  |                             "UseMatrix": "true" | ||||||
|  |                         }, | ||||||
|  |                         { | ||||||
|  |                             "DisplayName": "Watch Apple Tv", | ||||||
|  |                             "DeviceSetupList": [ | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|  |                                     "DeviceName": "Apple TV Gen 4", | ||||||
|  |                                     "Input": [] | ||||||
|  |                                 }, | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|  |                                     "DeviceName": "Westinghouse TV", | ||||||
|  |                                     "Input": "HDMI1" | ||||||
|  |                                 } | ||||||
|  |                             ], | ||||||
|  |                             "VolumeDevice": "Westinghouse TV", | ||||||
|  |                             "ControlDevice": "Apple TV Gen 4", | ||||||
|  |                             "OutputDevice": "Westinghouse TV", | ||||||
|  |                             "UseMatrix": "true" | ||||||
|  |                         }, | ||||||
|  |                         { | ||||||
|  |                             "DisplayName": "Play Xbox One", | ||||||
|  |                             "DeviceSetupList": [ | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|  |                                     "DeviceName": "Westinghouse TV", | ||||||
|  |                                     "Input": "HDMI1" | ||||||
|  |                                 }, | ||||||
|  |                                 { | ||||||
|  |                                     "Hub": "Harmony", | ||||||
|  |                                     "DeviceName": "Microsoft Xbox One", | ||||||
|  |                                     "Input": [] | ||||||
|  |                                 } | ||||||
|  |                             ], | ||||||
|  |                             "VolumeDevice": "Westinghouse TV", | ||||||
|  |                             "ControlDevice": "Microsoft Xbox One", | ||||||
|  |                             "OutputDevice": "Westinghouse TV", | ||||||
|  |                             "UseMatrix": "true" | ||||||
|  |                         } | ||||||
|  |                     ] | ||||||
|  |                 } | ||||||
|  |             ] | ||||||
|  |         } | ||||||
|  |     ] | ||||||
|  | } | ||||||
| @@ -3,8 +3,5 @@ | |||||||
| 		{ | 		{ | ||||||
| 			"path": "." | 			"path": "." | ||||||
| 		} | 		} | ||||||
|     ], | 	] | ||||||
|     "settings": { |  | ||||||
|         "editor.tabSize": 2 |  | ||||||
|     } |  | ||||||
| } | } | ||||||
							
								
								
									
										2072
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2072
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										21
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,13 +1,10 @@ | |||||||
| { | { | ||||||
|     "name": "@watsonb8/homebridge-harmony-control", |     "name": "homebridge-harmony-control", | ||||||
|     "version": "1.2.0", |     "version": "1.0.0", | ||||||
|     "description": "Homebridge platform to control smart home equipment by room.", |     "description": "Homebridge platform to control smart home equipment by room.", | ||||||
|     "main": "bin/index.js", |     "main": "bin/index.js", | ||||||
|     "publishConfig": { |  | ||||||
|         "registry": "http://10.44.1.6:4873/" |  | ||||||
|     }, |  | ||||||
|     "scripts": { |     "scripts": { | ||||||
|         "build": "tsc --build", |         "build": "tsc", | ||||||
|         "prepublishOnly": "npm run build" |         "prepublishOnly": "npm run build" | ||||||
|     }, |     }, | ||||||
|     "repository": { |     "repository": { | ||||||
| @@ -24,24 +21,24 @@ | |||||||
|         "homekit" |         "homekit" | ||||||
|     ], |     ], | ||||||
|     "engines": { |     "engines": { | ||||||
|         "homebridge": ">=1.1.6", |         "homebridge": ">=0.4.21", | ||||||
|         "node": ">=7.6.0" |         "node": ">=7.6.0" | ||||||
|     }, |     }, | ||||||
|     "devDependencies": { |     "devDependencies": { | ||||||
|         "homebridge": "^1.2.5", |  | ||||||
|         "rollup-plugin-typescript": "^1.0.1", |         "rollup-plugin-typescript": "^1.0.1", | ||||||
|         "tslib": "^1.10.0", |         "tslib": "^1.10.0", | ||||||
|         "tslint": "^5.17.0", |         "tslint": "^5.17.0", | ||||||
|         "typescript": "^3.9.10" |         "typescript": "^3.5.1" | ||||||
|     }, |     }, | ||||||
|     "dependencies": { |     "dependencies": { | ||||||
|         "@babel/core": "^7.4.5", |         "@babel/core": "^7.4.5", | ||||||
|         "@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", | ||||||
|         "reflect-metadata": "^0.1.13", |         "homebridge": "^0.4.50", | ||||||
|         "request": "^2.88.0", |         "node-fetch": "^2.6.0", | ||||||
|         "tsyringe": "^4.6.0" |         "request": "^2.88.0" | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										315
									
								
								src/Accessories/ControlUnit.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										315
									
								
								src/Accessories/ControlUnit.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,315 @@ | |||||||
|  | import { Activity } from '../Models/Activity'; | ||||||
|  | import { Matrix } from '../Models/Matrix'; | ||||||
|  | import { IAccessory } from './IAccessory'; | ||||||
|  | import callbackify from '../Util/Callbackify'; | ||||||
|  | import HubDataProvider from '../DataProviders/HubDataProvider'; | ||||||
|  |  | ||||||
|  | let Service: HAPNodeJS.Service; | ||||||
|  | let Characteristic: HAPNodeJS.Characteristic; | ||||||
|  | let Api: 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 { | ||||||
|  |     dataProvider: HubDataProvider, | ||||||
|  |     displayName: string, | ||||||
|  |     activities: Array<Activity>, | ||||||
|  |     api: any, | ||||||
|  |     log: any, | ||||||
|  |     homebridge: any, | ||||||
|  |     isExternal: boolean, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * ControlUnit accessory | ||||||
|  |  */ | ||||||
|  | export class ControlUnit implements IAccessory { | ||||||
|  |     //Required by homebridge | ||||||
|  |     name: string = ""; | ||||||
|  |  | ||||||
|  |     //fields | ||||||
|  |     private log: any = {}; | ||||||
|  |     private displayName: string = ""; | ||||||
|  |     private isExternal: boolean = false; | ||||||
|  |  | ||||||
|  |     //Service fields | ||||||
|  |     private televisionService: HAPNodeJS.Service | undefined; | ||||||
|  |     private televisionSpeakerService: HAPNodeJS.Service | undefined; | ||||||
|  |     private informationService: HAPNodeJS.Service | undefined; | ||||||
|  |     private inputServices: Array<HAPNodeJS.Service | undefined> = []; | ||||||
|  |  | ||||||
|  |     //Harmony fields | ||||||
|  |     private activities: Array<Activity> = []; | ||||||
|  |     private dataProvider: HubDataProvider; | ||||||
|  |  | ||||||
|  |     public platformAccessory: any; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Constructor | ||||||
|  |      * @param props Input properties | ||||||
|  |      */ | ||||||
|  |     constructor(props: IControlUnitProps) { | ||||||
|  |         //Assign class variables | ||||||
|  |         this.log = props.log; | ||||||
|  |         Api = props.api; | ||||||
|  |         Service = props.api.hap.Service; | ||||||
|  |         Characteristic = props.api.hap.Characteristic; | ||||||
|  |         this.name = props.displayName; | ||||||
|  |         this.displayName = props.isExternal ? `${props.displayName}-Remote` : props.displayName; | ||||||
|  |         this.isExternal = props.isExternal; | ||||||
|  |  | ||||||
|  |         this.activities = props.activities; | ||||||
|  |  | ||||||
|  |         this.dataProvider = props.dataProvider; | ||||||
|  |         homebridge = props.homebridge; | ||||||
|  |  | ||||||
|  |         this.platformAccessory = new homebridge.platformAccessory(this.displayName, this.generateUUID(), homebridge.hap.Accessory.Categories.TELEVISION); | ||||||
|  |  | ||||||
|  |         //Configure services | ||||||
|  |         this.configureTvService(); | ||||||
|  |  | ||||||
|  |         this.configureTvSpeakerService(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         // this.configureAccessoryInformation(); | ||||||
|  |         this.configureInputSourceService(); | ||||||
|  |  | ||||||
|  |         //Configure external services | ||||||
|  |         this.getServices().forEach(service => { | ||||||
|  |             try { | ||||||
|  |                 this.platformAccessory.addService(service); | ||||||
|  |             } catch (error) { } | ||||||
|  |  | ||||||
|  |             //@ts-ignore | ||||||
|  |             if (service.linked) { | ||||||
|  |                 //@ts-ignore | ||||||
|  |                 this.televisionService!.addLinkedService(service); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |     /************* | ||||||
|  |      *  | ||||||
|  |      * Tv Service | ||||||
|  |      *  | ||||||
|  |      *************/ | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Configure television service | ||||||
|  |      */ | ||||||
|  |     private configureTvService(): void { | ||||||
|  |         this.televisionService = new Service.Television( | ||||||
|  |             this.displayName, | ||||||
|  |             'tvService' | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         this.televisionService.setCharacteristic(Characteristic.ConfiguredName, this.displayName); | ||||||
|  |         //@ts-ignore | ||||||
|  |         this.televisionService.setCharacteristic(Characteristic.SleepDiscoveryMode, Characteristic.SleepDiscoveryMode.ALWAYS_DISCOVERABLE); | ||||||
|  |         this.televisionService.setCharacteristic(Characteristic.ActiveIdentifier, 1); | ||||||
|  |         this.televisionService.setCharacteristic(Characteristic.Active, false); | ||||||
|  |  | ||||||
|  |         //setup listeners | ||||||
|  |         this.televisionService.getCharacteristic(Characteristic.Active) | ||||||
|  |             //@ts-ignore | ||||||
|  |             .on("set", callbackify(this.onSetAccessoryActive)) | ||||||
|  |             //@ts-ignore | ||||||
|  |             .on("get", callbackify(this.onGetAccessoryActive)); | ||||||
|  |  | ||||||
|  |         //Set remote characteristics if is external | ||||||
|  |         if (this.isExternal) { | ||||||
|  |             this.televisionService.getCharacteristic(Characteristic.RemoteKey) | ||||||
|  |                 //@ts-ignore | ||||||
|  |                 .on("set", callbackify(this.onSetRemoteKey)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         this.televisionService.getCharacteristic(Characteristic.ActiveIdentifier) | ||||||
|  |             //@ts-ignore | ||||||
|  |             .on("set", callbackify(this.onSetActiveIdentifier)) | ||||||
|  |             //@ts-ignore | ||||||
|  |             .on("get", callbackify(this.onGetActiveIdentifier)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Event handler for SET active characteristic | ||||||
|  |      */ | ||||||
|  |     private onSetAccessoryActive = async (value: any) => { | ||||||
|  |         if (!this.isExternal) { | ||||||
|  |             switch (value) { | ||||||
|  |                 case 0: this.dataProvider.powerOff(this.name); break; | ||||||
|  |                 //Turn on with first activity | ||||||
|  |                 case 1: this.dataProvider.powerOn(this.name, this.activities[0]); break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Event handler for GET active characteristic | ||||||
|  |      */ | ||||||
|  |     private onGetAccessoryActive = async () => { | ||||||
|  |         //@ts-ignore | ||||||
|  |         return this.dataProvider.getIsActive() ? Characteristic.Active.Active : Characteristic.Active.Inactive | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Event handler for SET remote key | ||||||
|  |      */ | ||||||
|  |     private onSetRemoteKey = async (key: any) => { | ||||||
|  |         if (this.isExternal) { | ||||||
|  |             this.dataProvider.sendKeyPress(this.name, key); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Event handler for SET active identifier characteristic | ||||||
|  |      */ | ||||||
|  |     private onSetActiveIdentifier = async (identifier: any) => { | ||||||
|  |         if (!this.isExternal) { | ||||||
|  |             this.dataProvider.startActivity(this.name, this.activities[identifier]); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Event handler for GET active identifier characteristic | ||||||
|  |      */ | ||||||
|  |     private onGetActiveIdentifier = async () => { | ||||||
|  |         let currentActivity: Activity = this.dataProvider.getIsActive(this.name)!; | ||||||
|  |         let identifier: number = 0; | ||||||
|  |         if (currentActivity) { | ||||||
|  |             identifier = this.activities.findIndex(e => e.displayName === currentActivity.displayName); | ||||||
|  |         } | ||||||
|  |         return identifier; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /****************** | ||||||
|  |      *  | ||||||
|  |      * Speaker Service | ||||||
|  |      *  | ||||||
|  |      *****************/ | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Configure Speaker Service | ||||||
|  |      */ | ||||||
|  |     private configureTvSpeakerService(): void { | ||||||
|  |         this.televisionSpeakerService = new Service.TelevisionSpeaker( | ||||||
|  |             this.displayName, | ||||||
|  |             'tvSpeakerService' | ||||||
|  |         ); | ||||||
|  |         this.televisionSpeakerService.setCharacteristic(Characteristic.Name, this.displayName); | ||||||
|  |         //@ts-ignore | ||||||
|  |         this.televisionSpeakerService.setCharacteristic(Characteristic.Active, Characteristic.Active.ACTIVE); | ||||||
|  |         //@ts-ignore | ||||||
|  |         this.televisionSpeakerService.setCharacteristic(Characteristic.VolumeControlType, Characteristic.VolumeControlType.ABSOLUTE); | ||||||
|  |         this.televisionSpeakerService.subtype = this.displayName + "Volume"; | ||||||
|  |         if (this.televisionService) { | ||||||
|  |             //@ts-ignore | ||||||
|  |             this.televisionService.addLinkedService(this.televisionSpeakerService); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         //Setup listeners | ||||||
|  |         this.televisionSpeakerService.getCharacteristic(Characteristic.VolumeSelector) | ||||||
|  |             //@ts-ignore | ||||||
|  |             .on("set", callbackify(this.onSetVolumeSelector)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Event handler for SET volume characteristic | ||||||
|  |      */ | ||||||
|  |     private onSetVolumeSelector = async (value: any) => { | ||||||
|  |         if (this.isExternal) { | ||||||
|  |             switch (value) { | ||||||
|  |                 case 0: this.dataProvider.volumeUp(this.name); break; | ||||||
|  |                 case 1: this.dataProvider.volumeDown(this.name); break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /********************* | ||||||
|  |      *  | ||||||
|  |      * Information Service | ||||||
|  |      *  | ||||||
|  |      ********************/ | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Configure information service | ||||||
|  |      */ | ||||||
|  |     private configureAccessoryInformation(): void { | ||||||
|  |         this.informationService = new Service.AccessoryInformation(this.displayName, 'information'); | ||||||
|  |         this.informationService | ||||||
|  |             .setCharacteristic(Characteristic.Manufacturer, 'Loftux Carwings') | ||||||
|  |             .setCharacteristic(Characteristic.Model, 'Heater-Cooler') | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /***************** | ||||||
|  |      *  | ||||||
|  |      * Input services | ||||||
|  |      *  | ||||||
|  |      *****************/ | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Configure input service | ||||||
|  |      */ | ||||||
|  |     private configureInputSourceService(): void { | ||||||
|  |         let inputs: Array<HAPNodeJS.Service> = []; | ||||||
|  |         this.activities.forEach((activity: Activity, index: number) => { | ||||||
|  |             let inputService = new Service.InputSource(activity.displayName, 'activity' + activity.displayName); | ||||||
|  |             inputService | ||||||
|  |                 .setCharacteristic(Characteristic.Identifier, index) | ||||||
|  |                 .setCharacteristic( | ||||||
|  |                     Characteristic.ConfiguredName, | ||||||
|  |                     activity.displayName) | ||||||
|  |                 .setCharacteristic( | ||||||
|  |                     Characteristic.IsConfigured, | ||||||
|  |                     //@ts-ignore | ||||||
|  |                     Characteristic.IsConfigured.CONFIGURED | ||||||
|  |                 ) | ||||||
|  |                 //@ts-ignore | ||||||
|  |                 .setCharacteristic(Characteristic.InputSourceType, Characteristic.InputSourceType.HDMI); | ||||||
|  |  | ||||||
|  |             //@ts-ignore | ||||||
|  |             this.televisionService!.addLinkedService(inputService); | ||||||
|  |             inputs.push(inputService); | ||||||
|  |         }); | ||||||
|  |         this.inputServices = inputs; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private generateUUID(): string { // Public Domain/MIT | ||||||
|  |         var d = new Date().getTime(); | ||||||
|  |         if (typeof performance !== 'undefined' && typeof performance.now === 'function') { | ||||||
|  |             d += performance.now(); //use high-precision timer if available | ||||||
|  |         } | ||||||
|  |         return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { | ||||||
|  |             var r = (d + Math.random() * 16) % 16 | 0; | ||||||
|  |             d = Math.floor(d / 16); | ||||||
|  |             return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Called by homebridge to gather services for this accessory. | ||||||
|  |      */ | ||||||
|  |     getServices(): Array<HAPNodeJS.Service> { | ||||||
|  |         let services: Array<HAPNodeJS.Service> = [this.televisionService!, this.televisionSpeakerService!]; | ||||||
|  |         this.inputServices.forEach((service: HAPNodeJS.Service | undefined) => { | ||||||
|  |             services.push(service!); | ||||||
|  |         }); | ||||||
|  |         return (services); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								src/Accessories/IAccessory.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/Accessories/IAccessory.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Interface to describe homebridge required elements. | ||||||
|  |  */ | ||||||
|  | export interface IAccessory { | ||||||
|  |     /** | ||||||
|  |      * Required by homebridge. | ||||||
|  |      */ | ||||||
|  |     name: string, | ||||||
|  |     /** | ||||||
|  |      * Called by homebridge to gather services. | ||||||
|  |      */ | ||||||
|  |     getServices(): Array<HAPNodeJS.Service>, | ||||||
|  | } | ||||||
							
								
								
									
										508
									
								
								src/DataProviders/HubDataProvider.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										508
									
								
								src/DataProviders/HubDataProvider.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,508 @@ | |||||||
|  | import { Activity } from "../Models/Activity"; | ||||||
|  | import { DeviceSetupItem } from "../Models/DeviceSetupItem"; | ||||||
|  | import { Input, Matrix, Output } from "../Models/Matrix"; | ||||||
|  | import { IDevice } from './IDevice'; | ||||||
|  | import { RemoteKey } from '../Accessories/ControlUnit'; | ||||||
|  | import { sleep } from '../Util/Sleep'; | ||||||
|  | import { hub } from ".."; | ||||||
|  | import fetch from 'node-fetch'; | ||||||
|  |  | ||||||
|  | let Characteristic: HAPNodeJS.Characteristic; | ||||||
|  |  | ||||||
|  | const Harmony = require("harmony-websocket"); | ||||||
|  |  | ||||||
|  | interface IActivityState { | ||||||
|  |     currentActivity: Activity | ||||||
|  | } | ||||||
|  |  | ||||||
|  | interface IHubDataProviderProps { | ||||||
|  |     harmonyHubAddress: string, | ||||||
|  |     neeoHubAddress: string, | ||||||
|  |     log: any, | ||||||
|  |     matrix: Matrix | ||||||
|  | } | ||||||
|  |  | ||||||
|  | interface INeeoMacro { | ||||||
|  |     roomKey: string, | ||||||
|  |     macroKey: string, | ||||||
|  |     deviceKey: string, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class HubDataProvider { | ||||||
|  |     //Harmony Fields | ||||||
|  |     private harmonyHubAddress: string; | ||||||
|  |     private harmonyConnected: boolean = false; | ||||||
|  |  | ||||||
|  |     //Neeo Fields | ||||||
|  |     private neeoHubAddress: string; | ||||||
|  |  | ||||||
|  |     //Common Fields | ||||||
|  |     private log: any; | ||||||
|  |  | ||||||
|  |     private devices: { [name: string]: IDevice; } = {}; | ||||||
|  |     private states: { [controlUnitName: string]: (IActivityState | undefined) } = {}; | ||||||
|  |  | ||||||
|  |     private matrix: Matrix; | ||||||
|  |  | ||||||
|  |     constructor(props: IHubDataProviderProps) { | ||||||
|  |         this.log = props.log; | ||||||
|  |         this.harmonyHubAddress = props.harmonyHubAddress; | ||||||
|  |         this.neeoHubAddress = props.neeoHubAddress; | ||||||
|  |         this.matrix = props.matrix; | ||||||
|  |  | ||||||
|  |         if (this.harmonyHubAddress) { | ||||||
|  |             try { | ||||||
|  |                 Promise.resolve(this.connectHarmony(this.harmonyHubAddress)); | ||||||
|  |             } catch (err) { | ||||||
|  |                 this.log(`Error connecting to harmony hub at ${this.harmonyHubAddress}: ${err}`); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (this.neeoHubAddress) { | ||||||
|  |             try { | ||||||
|  |                 Promise.resolve(this.connectNeeo(this.neeoHubAddress)); | ||||||
|  |             } catch (err) { | ||||||
|  |                 this.log(`Error connecting to neeo hub at ${this.neeoHubAddress}: ${err}`) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Power on all devices in an activity. | ||||||
|  |      */ | ||||||
|  |     public powerOn = async (controlUnitName: string, activity: Activity) => { | ||||||
|  |         //Only power on if not alread on | ||||||
|  |         let currentActivity = this.states[controlUnitName] ? this.states[controlUnitName]!.currentActivity : undefined; | ||||||
|  |         if (!currentActivity) { | ||||||
|  |             await this.startActivity(controlUnitName, activity); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Power off all devices in an activity that aren't being used. | ||||||
|  |      */ | ||||||
|  |     public powerOff = async (controlUnitName: string) => { | ||||||
|  |         if (!this.states[controlUnitName]) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         //Build potential list of devices to turn off | ||||||
|  |         let devicesToTurnOff: Array<IDevice> = this.states[controlUnitName]!.currentActivity.deviceSetupItems | ||||||
|  |             .map((value: DeviceSetupItem): IDevice => { | ||||||
|  |                 return this.getDeviceFromName(value.deviceName, value.hubType); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |         //Resolve device conflicts with other controlUnits | ||||||
|  |         devicesToTurnOff = this.sanitizeDeviceList(devicesToTurnOff, controlUnitName); | ||||||
|  |  | ||||||
|  |         await Promise.all( | ||||||
|  |             //Turn off devices | ||||||
|  |             devicesToTurnOff.map(async (device: IDevice) => { | ||||||
|  |                 if (device) { | ||||||
|  |                     await device.powerOff(); | ||||||
|  |                 } | ||||||
|  |             }) | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         this.states[controlUnitName] = undefined; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Start an activity | ||||||
|  |      */ | ||||||
|  |     public startActivity = async (controlUnitName: string, activity: Activity) => { | ||||||
|  |         this.log(`Starting activity ${activity.displayName} for controlUnit: ${controlUnitName}`) | ||||||
|  |         let lastActivity: Activity | undefined = undefined; | ||||||
|  |         if (this.states[controlUnitName]) { | ||||||
|  |             lastActivity = this.states[controlUnitName]!.currentActivity; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         //Build potential list of devices to to turn on | ||||||
|  |         let devicesToTurnOn: Array<IDevice> = activity.deviceSetupItems.map((value: DeviceSetupItem): IDevice => { | ||||||
|  |             return this.getDeviceFromName(value.deviceName, value.hubType); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         //Resolve device conflicts with other controlUnits | ||||||
|  |         devicesToTurnOn = this.sanitizeDeviceList(devicesToTurnOn, controlUnitName); | ||||||
|  |  | ||||||
|  |         //Turn on devices | ||||||
|  |         await Promise.all(devicesToTurnOn.map(async (device: IDevice) => { | ||||||
|  |             if (device && device.name) { | ||||||
|  |                 if (!device.on) { | ||||||
|  |                     this.log(`Turning on device ${device.name}`) | ||||||
|  |                     await device.powerOn(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         })); | ||||||
|  |  | ||||||
|  |         //Assign correct input | ||||||
|  |         await Promise.all( | ||||||
|  |             activity.deviceSetupItems.map(async (value: DeviceSetupItem) => { | ||||||
|  |                 let device: IDevice = this.getDeviceFromName(value.deviceName, value.hubType); | ||||||
|  |  | ||||||
|  |                 if (device && device.supportsCommand(`Input ${value.input}`)) { | ||||||
|  |                     let command: string = device.getCommand(`Input ${value.input}`); | ||||||
|  |                     await device.sendCommand(command); | ||||||
|  |                 } | ||||||
|  |             }) | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         if (activity.useMatrix) { | ||||||
|  |             //get input and output | ||||||
|  |             let input: Input = this.matrix.inputs.filter(e => e.inputDevice === activity.controlDeviceId)[0]; | ||||||
|  |             let output: Output = this.matrix.outputs.filter(e => e.outputDevice === activity.outputDeviceId)[0]; | ||||||
|  |  | ||||||
|  |             let inputCommandName: string = `In ${input.inputNumber}`; | ||||||
|  |             let outputCommandName: string = `Out ${output.outputLetter}`; | ||||||
|  |  | ||||||
|  |             let matrixDevice: IDevice = this.getDeviceFromName(this.matrix.deviceName, this.matrix.hubType); | ||||||
|  |  | ||||||
|  |             //Route hdmi | ||||||
|  |             if (matrixDevice.supportsCommand(inputCommandName) && matrixDevice.supportsCommand(outputCommandName)) { | ||||||
|  |                 await matrixDevice.sendCommand(matrixDevice.getCommand(inputCommandName)); | ||||||
|  |                 await matrixDevice.sendCommand(matrixDevice.getCommand(outputCommandName)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         //Build potential list of devices to turn off | ||||||
|  |         if (lastActivity) { | ||||||
|  |             let devicesToTurnOff: Array<IDevice> = lastActivity.deviceSetupItems.map((value: DeviceSetupItem): IDevice => { | ||||||
|  |                 return this.getDeviceFromName(value.deviceName, value.hubType); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             //remove devices that will be used for next activity from list | ||||||
|  |             //delete array[index] is stupid because it just nulls out the index. But now i have to deal with nulls | ||||||
|  |             devicesToTurnOff.forEach((device: IDevice, index: number) => { | ||||||
|  |                 if (device && device.name && activity.deviceSetupItems.some(e => { | ||||||
|  |                     return (e && e.deviceName === device.name) | ||||||
|  |                 })) { | ||||||
|  |                     delete devicesToTurnOff[index]; | ||||||
|  |                 } | ||||||
|  |             }) | ||||||
|  |  | ||||||
|  |             //Resolve device conflicts with other controlUnits | ||||||
|  |             devicesToTurnOff = this.sanitizeDeviceList(devicesToTurnOff, controlUnitName); | ||||||
|  |  | ||||||
|  |             this.log(`Sanatized devices to turn off: ${JSON.stringify(devicesToTurnOff.map(e => e ? e.name : ""))}`); | ||||||
|  |  | ||||||
|  |             await Promise.all( | ||||||
|  |                 //Turn off devices | ||||||
|  |                 devicesToTurnOff.map(async (device: IDevice) => { | ||||||
|  |                     if (device) { | ||||||
|  |                         if (device.on) { | ||||||
|  |                             this.log(`Turning off device ${device.name}`) | ||||||
|  |                             await device.powerOff(); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 }) | ||||||
|  |             ); | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         //Assign current activity | ||||||
|  |         this.states[controlUnitName] = { currentActivity: activity }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Turn the volume up for the current running activity. | ||||||
|  |      */ | ||||||
|  |     public volumeUp = async (controlUnitName: string) => { | ||||||
|  |         let volumeUpCommand: string = "Volume Up" | ||||||
|  |         let currentActivity: Activity = this.states[controlUnitName]!.currentActivity; | ||||||
|  |         let deviceSetupItem: DeviceSetupItem = currentActivity.deviceSetupItems.filter(e => e.deviceName == currentActivity.volumeDeviceId)[0]; | ||||||
|  |  | ||||||
|  |         if (this.states[controlUnitName]) { | ||||||
|  |             let volumeDevice: IDevice = this.getDeviceFromName(currentActivity.volumeDeviceId, deviceSetupItem.hubType); | ||||||
|  |             if (volumeDevice.supportsCommand(volumeUpCommand)) { | ||||||
|  |                 volumeDevice.sendCommand(volumeDevice.getCommand(volumeUpCommand)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Volume down for current running activity. | ||||||
|  |      */ | ||||||
|  |     public volumeDown = async (controlUnitName: string) => { | ||||||
|  |         let volumeDownCommand: string = "Volume Down" | ||||||
|  |         let currentActivity: Activity = this.states[controlUnitName]!.currentActivity; | ||||||
|  |         let deviceSetupItem: DeviceSetupItem = currentActivity.deviceSetupItems.filter(e => e.deviceName == currentActivity.volumeDeviceId)[0]; | ||||||
|  |  | ||||||
|  |         if (this.states[controlUnitName]) { | ||||||
|  |             let volumeDevice: IDevice = this.getDeviceFromName(currentActivity.volumeDeviceId, deviceSetupItem.hubType); | ||||||
|  |             if (volumeDevice.supportsCommand(volumeDownCommand)) { | ||||||
|  |                 volumeDevice.sendCommand(volumeDevice.getCommand(volumeDownCommand)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Return if a control unit is active | ||||||
|  |      * @param controlUnitName  | ||||||
|  |      */ | ||||||
|  |     public getIsActive(controlUnitName: string): Activity | undefined { | ||||||
|  |         return this.states[controlUnitName] ? this.states[controlUnitName]!.currentActivity : undefined; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Helper function to make sure no control unit depends on device list. | ||||||
|  |      * @param devicesToTurnOn The list of devices to modify. | ||||||
|  |      * @param controlUnitName The name of the control unit in question. | ||||||
|  |      */ | ||||||
|  |     private sanitizeDeviceList(devicesToTurnOn: Array<IDevice>, controlUnitName: string): Array<IDevice> { | ||||||
|  |         for (let controlUnitKey in this.states) { | ||||||
|  |             //Skip self | ||||||
|  |             if (controlUnitKey === controlUnitName) { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |             let currentOtherState: IActivityState = this.states[controlUnitKey]!; | ||||||
|  |  | ||||||
|  |             if (currentOtherState) { | ||||||
|  |                 currentOtherState.currentActivity.deviceSetupItems.forEach((value: DeviceSetupItem) => { | ||||||
|  |                     //there are devices to remove | ||||||
|  |                     if (devicesToTurnOn.some(e => e && e.name === value.deviceName)) { | ||||||
|  |                         let deviceToRemove: IDevice = devicesToTurnOn.filter(i => i.name === value.deviceName)[0]; | ||||||
|  |                         delete devicesToTurnOn[devicesToTurnOn.indexOf(deviceToRemove)]; | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return devicesToTurnOn; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Send key press for current activity. | ||||||
|  |      *  | ||||||
|  |      * @param controlUnitName The name of the control unit to act on. | ||||||
|  |      * @param key The key to send. | ||||||
|  |      */ | ||||||
|  |     public sendKeyPress = async (controlUnitName: string, key: any) => { | ||||||
|  |         if (this.states[controlUnitName]) { | ||||||
|  |             let commandName: string = ""; | ||||||
|  |  | ||||||
|  |             let currentActivity: Activity = this.states[controlUnitName]!.currentActivity; | ||||||
|  |             let deviceSetupItem: DeviceSetupItem = currentActivity.deviceSetupItems.filter(e => e.deviceName == currentActivity.volumeDeviceId)[0]; | ||||||
|  |  | ||||||
|  |             let device: IDevice = this.getDeviceFromName(this.states[controlUnitName]!.currentActivity.controlDeviceId, deviceSetupItem.hubType); | ||||||
|  |             switch (key) { | ||||||
|  |                 case RemoteKey.ARROW_UP: { | ||||||
|  |                     commandName = "Direction Up"; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 case RemoteKey.ARROW_DOWN: { | ||||||
|  |                     commandName = "Direction Down"; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 case RemoteKey.ARROW_LEFT: { | ||||||
|  |                     commandName = "Direction Left"; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 case RemoteKey.ARROW_RIGHT: { | ||||||
|  |                     commandName = "Direction Right"; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 case RemoteKey.SELECT: { | ||||||
|  |                     commandName = "Select"; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 case RemoteKey.PLAY_PAUSE: { | ||||||
|  |                     commandName = "Pause"; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 case RemoteKey.INFORMATION: { | ||||||
|  |                     commandName = "Menu"; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 case RemoteKey.BACK: { | ||||||
|  |                     commandName = "Back"; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 case RemoteKey.EXIT: { | ||||||
|  |                     commandName = "Back"; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (device && device.supportsCommand(commandName)) { | ||||||
|  |                 device.sendCommand(device.getCommand(commandName)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Get the IDevice by name. | ||||||
|  |      * @param deviceName The device to retrieve. | ||||||
|  |      */ | ||||||
|  |     private getDeviceFromName(deviceName: string, hubType: hub): IDevice { | ||||||
|  |         return this.devices[`${hubType}-${deviceName}`]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Connect to harmony hub and receive device info. | ||||||
|  |      */ | ||||||
|  |     private connectHarmony = async (address: string): Promise<void> => { | ||||||
|  |         let harmony: any = new Harmony(); | ||||||
|  |         //Listeners | ||||||
|  |         harmony.on('open', () => { | ||||||
|  |             this.harmonyConnected = true; | ||||||
|  |         }); | ||||||
|  |         harmony.on('close', () => { | ||||||
|  |             this.harmonyConnected = false; | ||||||
|  |         }); | ||||||
|  |         await harmony.connect(address); | ||||||
|  |         let self = this; | ||||||
|  |  | ||||||
|  |         setTimeout(async function () { | ||||||
|  |             if (self.harmonyConnected) { | ||||||
|  |                 let devices: any = await harmony.getDevices(); | ||||||
|  |                 try { | ||||||
|  |                     await Promise.all( | ||||||
|  |                         //Add each to dictionary | ||||||
|  |                         devices.map(async (dev: any) => { | ||||||
|  |                             //get commands | ||||||
|  |                             let commands: { [name: string]: string } = {}; | ||||||
|  |                             let deviceCommands: any = await harmony.getDeviceCommands(dev.id); | ||||||
|  |                             deviceCommands.forEach((command: any) => { | ||||||
|  |                                 commands[command.label] = command.action; | ||||||
|  |                             }); | ||||||
|  |                             self.devices[`Harmony-${dev.label}`] = { | ||||||
|  |                                 id: dev.id, | ||||||
|  |                                 name: dev.label, | ||||||
|  |                                 commands: commands, | ||||||
|  |                                 harmony: harmony, | ||||||
|  |                                 hubType: "Harmony", | ||||||
|  |                                 log: self.log, | ||||||
|  |                                 on: false, | ||||||
|  |                                 //Define device methods | ||||||
|  |                                 supportsCommand(commandName: string): boolean { | ||||||
|  |                                     let command = commands[commandName]; | ||||||
|  |                                     return (command) ? true : false; | ||||||
|  |                                 }, | ||||||
|  |                                 getCommand(commandName: string): string { | ||||||
|  |                                     return commands[commandName]; | ||||||
|  |                                 }, | ||||||
|  |                                 async powerOn(): Promise<void> { | ||||||
|  |                                     let powerOnCommand: string = "Power On"; | ||||||
|  |                                     let powerToggleCommand: string = "Power Toggle"; | ||||||
|  |                                     if (this.supportsCommand(powerOnCommand)) { | ||||||
|  |                                         await this.sendCommand(this.getCommand(powerOnCommand)); | ||||||
|  |                                         this.on = true; | ||||||
|  |                                     } else if (this.supportsCommand(powerToggleCommand)) { | ||||||
|  |                                         await this.sendCommand(this.getCommand(powerToggleCommand)); | ||||||
|  |                                         this.on = true; | ||||||
|  |                                     } | ||||||
|  |                                 }, | ||||||
|  |                                 async powerOff(): Promise<void> { | ||||||
|  |                                     let powerOffCommand: string = "Power Off"; | ||||||
|  |                                     let powerToggleCommand: string = "Power Toggle"; | ||||||
|  |                                     if (this.supportsCommand(powerOffCommand)) { | ||||||
|  |                                         await this.sendCommand(this.getCommand(powerOffCommand)); | ||||||
|  |                                         this.on = false; | ||||||
|  |                                     } else if (this.supportsCommand(powerToggleCommand)) { | ||||||
|  |                                         await this.sendCommand(this.getCommand(powerToggleCommand)); | ||||||
|  |                                         this.on = false; | ||||||
|  |                                     } | ||||||
|  |                                 }, | ||||||
|  |                                 async sendCommand(command: string): Promise<void> { | ||||||
|  |                                     try { | ||||||
|  |                                         //Execute command | ||||||
|  |                                         let response = await this.harmony.sendCommand(JSON.stringify(command)); | ||||||
|  |  | ||||||
|  |                                         //Sleep | ||||||
|  |                                         await sleep(800); | ||||||
|  |                                     } catch (err) { | ||||||
|  |                                         this.log(`ERROR - error sending command to harmony: ${err}`); | ||||||
|  |                                     } | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                         })); | ||||||
|  |                     self.log(`Harmony data provider ready`); | ||||||
|  |  | ||||||
|  |                 } catch (err) { | ||||||
|  |                     self.log(`ERROR - error connecting to harmony: ${err}`); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }, 1000); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private connectNeeo = async (address: string): Promise<void> => { | ||||||
|  |  | ||||||
|  |         let baseUrl = `http://${address}:3000/v1/projects/home`; | ||||||
|  |  | ||||||
|  |         let response: any = await fetch(`${baseUrl}/rooms`); | ||||||
|  |         let rooms: { [key: string]: any } = await response.json(); | ||||||
|  |  | ||||||
|  |         function regularizeString(str: string) { | ||||||
|  |             str = str.toLowerCase(); | ||||||
|  |             return str.replace(/(?:^|\s)\S/g, function (a) { return a.toUpperCase(); }); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         for (let key in rooms) { | ||||||
|  |             let devices: { [key: string]: any } = rooms[key].devices; | ||||||
|  |             for (let deviceKey in devices) { | ||||||
|  |                 let device = devices[deviceKey]; | ||||||
|  |  | ||||||
|  |                 //shape commands from macros | ||||||
|  |                 let commands: { [item: string]: any } = {}; | ||||||
|  |                 for (let macroKey in device.macros) { | ||||||
|  |                     let macro: any = device.macros[macroKey]; | ||||||
|  |                     commands[regularizeString(macro.name)] = { | ||||||
|  |                         deviceKey: macro.deviceKey, | ||||||
|  |                         macroKey: macro.key, | ||||||
|  |                         roomKey: macro.roomKey, | ||||||
|  |                     }; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 this.devices[`Neeo-${deviceKey}`] = { | ||||||
|  |                     id: device.key, | ||||||
|  |                     name: device.name, | ||||||
|  |                     log: this.log, | ||||||
|  |                     hubType: "Neeo", | ||||||
|  |                     commands: commands, | ||||||
|  |                     on: false, | ||||||
|  |                     supportsCommand(commandName: string): boolean { | ||||||
|  |                         let command = this.commands[commandName]; | ||||||
|  |                         return (command) ? true : false; | ||||||
|  |                     }, | ||||||
|  |  | ||||||
|  |                     getCommand(commandName: string): string { | ||||||
|  |                         return this.commands[commandName]; | ||||||
|  |                     }, | ||||||
|  |  | ||||||
|  |                     async sendCommand(command: string): Promise<void> { | ||||||
|  |                         if (this.supportsCommand(command)) { | ||||||
|  |                             let macro: INeeoMacro = this.commands[command]; | ||||||
|  |                             let url: string = `${baseUrl}/rooms/${macro.roomKey}/devices/${macro.deviceKey}/macros/${macro.macroKey}/trigger`; | ||||||
|  |                             this.log(`Sending command ${url}`); | ||||||
|  |                             fetch(`${baseUrl}/rooms/${macro.roomKey}/devices/${macro.deviceKey}/macros/${macro.macroKey}/trigger`); | ||||||
|  |                         } | ||||||
|  |                     }, | ||||||
|  |  | ||||||
|  |                     async powerOn(): Promise<void> { | ||||||
|  |                         let powerOnCommand: string = "Power On"; | ||||||
|  |                         let powerToggleCommand: string = "Power Toggle"; | ||||||
|  |                         if (this.supportsCommand(powerOnCommand)) { | ||||||
|  |                             await this.sendCommand(powerOnCommand); | ||||||
|  |                             this.on = true; | ||||||
|  |                         } else if (this.supportsCommand(powerToggleCommand)) { | ||||||
|  |                             await this.sendCommand(powerToggleCommand); | ||||||
|  |                             this.on = true; | ||||||
|  |                         } | ||||||
|  |                     }, | ||||||
|  |                     async powerOff(): Promise<void> { | ||||||
|  |                         let powerOffCommand: string = "Power Off"; | ||||||
|  |                         let powerToggleCommand: string = "Power Toggle"; | ||||||
|  |                         if (this.supportsCommand(powerOffCommand)) { | ||||||
|  |                             await this.sendCommand(powerOffCommand); | ||||||
|  |                             this.on = false; | ||||||
|  |                         } else if (this.supportsCommand(powerToggleCommand)) { | ||||||
|  |                             await this.sendCommand(powerToggleCommand); | ||||||
|  |                             this.on = false; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export default HubDataProvider; | ||||||
							
								
								
									
										16
									
								
								src/DataProviders/IDevice.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/DataProviders/IDevice.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | import { hub } from ".."; | ||||||
|  |  | ||||||
|  | export interface IDevice { | ||||||
|  |     id: string, | ||||||
|  |     name: string, | ||||||
|  |     harmony?: any, | ||||||
|  |     log: any, | ||||||
|  |     hubType: hub | ||||||
|  |     supportsCommand(commandName: string): boolean, | ||||||
|  |     getCommand(commandName: string): string, | ||||||
|  |     sendCommand(command: string): void, | ||||||
|  |     powerOn(): Promise<void>, | ||||||
|  |     powerOff(): Promise<void>, | ||||||
|  |     commands: { [name: string]: any }; | ||||||
|  |     on: boolean; | ||||||
|  | } | ||||||
							
								
								
									
										69
									
								
								src/Models/Activity.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/Models/Activity.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | |||||||
|  | import { DeviceSetupItem } from './DeviceSetupItem'; | ||||||
|  | /** | ||||||
|  |  * Input properties. | ||||||
|  |  */ | ||||||
|  | export interface IActivityProps { | ||||||
|  |     deviceList: Array<DeviceSetupItem>, | ||||||
|  |     controlDeviceId: string, | ||||||
|  |     volumeDeviceId: string, | ||||||
|  |     outputDeviceId: string, | ||||||
|  |     displayName: string | ||||||
|  |     useMatrix: boolean, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Data model class to hold activity related information. | ||||||
|  |  */ | ||||||
|  | export class Activity { | ||||||
|  |     private _volumeDeviceId: string = ""; | ||||||
|  |     private _outputDeviceId: string = ""; | ||||||
|  |     private _controlDeviceId: string = ""; | ||||||
|  |     private _displayName: string = ""; | ||||||
|  |     private _deviceSetupItems: Array<DeviceSetupItem>; | ||||||
|  |     private _useMatrix: boolean = false; | ||||||
|  |  | ||||||
|  |     constructor(props: IActivityProps) { | ||||||
|  |         this._controlDeviceId = props.controlDeviceId; | ||||||
|  |         this._outputDeviceId = props.outputDeviceId; | ||||||
|  |         this._volumeDeviceId = props.volumeDeviceId; | ||||||
|  |         this._displayName = props.displayName; | ||||||
|  |         this._deviceSetupItems = props.deviceList; | ||||||
|  |         this._useMatrix = props.useMatrix | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * The device associated with main control. | ||||||
|  |      */ | ||||||
|  |     public get controlDeviceId(): string { | ||||||
|  |         return this._controlDeviceId; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * The device associated with the volume control. | ||||||
|  |      */ | ||||||
|  |     public get volumeDeviceId(): string { | ||||||
|  |         return this._volumeDeviceId | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * The device associated with output. | ||||||
|  |      */ | ||||||
|  |     public get outputDeviceId(): string { | ||||||
|  |         return this._outputDeviceId; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * The display name of the activity. | ||||||
|  |      */ | ||||||
|  |     public get displayName(): string { | ||||||
|  |         return this._displayName; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public get deviceSetupItems(): Array<DeviceSetupItem> { | ||||||
|  |         return this._deviceSetupItems | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public get useMatrix(): boolean { | ||||||
|  |         return this._useMatrix; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										33
									
								
								src/Models/DeviceSetupItem.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/Models/DeviceSetupItem.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | |||||||
|  | import { hub } from ".."; | ||||||
|  |  | ||||||
|  | export interface IDeviceSetupItemProps { | ||||||
|  |     deviceName: string, | ||||||
|  |     input: string, | ||||||
|  |     hub: hub | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Data model to hold device setup items. | ||||||
|  |  */ | ||||||
|  | export class DeviceSetupItem { | ||||||
|  |     private _deviceId: string = ""; | ||||||
|  |     private _input: string = ""; | ||||||
|  |     private _hubType: hub; | ||||||
|  |     constructor(props: IDeviceSetupItemProps) { | ||||||
|  |         this._deviceId = props.deviceName; | ||||||
|  |         this._input = props.input; | ||||||
|  |         this._hubType = props.hub; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public get deviceName() { | ||||||
|  |         return this._deviceId; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public get input() { | ||||||
|  |         return this._input; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public get hubType() { | ||||||
|  |         return this._hubType; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										51
									
								
								src/Models/Matrix.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/Models/Matrix.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | |||||||
|  | import { hub } from ".."; | ||||||
|  |  | ||||||
|  | export interface IMatrixProps { | ||||||
|  |     inputs: Array<Input>, | ||||||
|  |     outputs: Array<Output>, | ||||||
|  |     deviceName: string, | ||||||
|  |     hubType: hub | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export interface Input { | ||||||
|  |     inputNumber: string, | ||||||
|  |     inputDevice: string, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export interface Output { | ||||||
|  |     outputLetter: string, | ||||||
|  |     outputDevice: string, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Data model to hold matrix information. | ||||||
|  |  */ | ||||||
|  | export class Matrix { | ||||||
|  |     private _inputs: Array<Input> = []; | ||||||
|  |     private _outputs: Array<Output> = []; | ||||||
|  |     private _deviceName: string; | ||||||
|  |     private _hubType: hub = "Harmony"; | ||||||
|  |  | ||||||
|  |     constructor(props: IMatrixProps) { | ||||||
|  |         this._inputs = props.inputs; | ||||||
|  |         this._outputs = props.outputs; | ||||||
|  |         this._deviceName = props.deviceName; | ||||||
|  |         this._hubType = props.hubType; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public get inputs(): Array<Input> { | ||||||
|  |         return this._inputs | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public get outputs(): Array<Output> { | ||||||
|  |         return this._outputs; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public get deviceName(): string { | ||||||
|  |         return this._deviceName; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public get hubType(): hub { | ||||||
|  |         return this._hubType; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										456
									
								
								src/Types/HAPNodeJS.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										456
									
								
								src/Types/HAPNodeJS.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,456 @@ | |||||||
|  | declare namespace HAPNodeJS { | ||||||
|  |  | ||||||
|  |     export interface uuid { | ||||||
|  |         generate(data: string): string; | ||||||
|  |         isValid(UUID: string): boolean; | ||||||
|  |         unparse(bug: string, offset: number): string; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     type EventService = "characteristic-change" | "service-configurationChange" | ||||||
|  |  | ||||||
|  |     export interface IEventEmitterAccessory { | ||||||
|  |         addListener(event: EventService, listener: Function): this; | ||||||
|  |         on(event: EventService, listener: Function): this; | ||||||
|  |         once(event: EventService, listener: Function): this; | ||||||
|  |         removeListener(event: EventService, listener: Function): this; | ||||||
|  |         removeAllListeners(event?: EventService): this; | ||||||
|  |         setMaxListeners(n: number): this; | ||||||
|  |         getMaxListeners(): number; | ||||||
|  |         listeners(event: EventService): Function[]; | ||||||
|  |         emit(event: EventService, ...args: any[]): boolean; | ||||||
|  |         listenerCount(type: string): number; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     export interface Service extends IEventEmitterAccessory { | ||||||
|  |         new(displayName: string, UUID: string, subtype: string): Service; | ||||||
|  |  | ||||||
|  |         displayName: string; | ||||||
|  |         UUID: string; | ||||||
|  |         subtype: string; | ||||||
|  |         iid: string; | ||||||
|  |         characteristics: Characteristic[]; | ||||||
|  |         optionalCharacteristics: Characteristic[]; | ||||||
|  |  | ||||||
|  |         addCharacteristic(characteristic: Characteristic | Function): Characteristic; | ||||||
|  |         removeCharacteristic(characteristic: Characteristic): void; | ||||||
|  |         getCharacteristic(name: string | Function): Characteristic; | ||||||
|  |         testCharacteristic(name: string | Function): boolean; | ||||||
|  |         setCharacteristic(name: string | Function, value: CharacteristicValue): Service; | ||||||
|  |         updateCharacteristic(name: string | Function, value: CharacteristicValue): Service; | ||||||
|  |         addOptionalCharacteristic(characteristic: Characteristic | Function): void; | ||||||
|  |         getCharacteristicByIID(iid: string): Characteristic; | ||||||
|  |  | ||||||
|  |         toHAP(opt: any): JSON; | ||||||
|  |  | ||||||
|  |         AccessoryInformation: PredefinedService; | ||||||
|  |         AirPurifier: PredefinedService; | ||||||
|  |         AirQualitySensor: PredefinedService; | ||||||
|  |         BatteryService: PredefinedService; | ||||||
|  |         BridgeConfiguration: PredefinedService; | ||||||
|  |         BridgingState: PredefinedService; | ||||||
|  |         CameraControl: PredefinedService; | ||||||
|  |         CameraRTPStreamManagement: PredefinedService; | ||||||
|  |         CarbonDioxideSensor: PredefinedService; | ||||||
|  |         CarbonMonoxideSensor: PredefinedService; | ||||||
|  |         ContactSensor: PredefinedService; | ||||||
|  |         Door: PredefinedService; | ||||||
|  |         Doorbell: PredefinedService; | ||||||
|  |         Fan: PredefinedService; | ||||||
|  |         Fanv2: PredefinedService; | ||||||
|  |         Faucet: PredefinedService; | ||||||
|  |         FilterMaintenance: PredefinedService; | ||||||
|  |         GarageDoorOpener: PredefinedService; | ||||||
|  |         HeaterCooler: PredefinedService; | ||||||
|  |         HumidifierDehumidifier: PredefinedService; | ||||||
|  |         HumiditySensor: PredefinedService; | ||||||
|  |         InputSource: PredefinedService; | ||||||
|  |         IrrigationSystem: PredefinedService; | ||||||
|  |         LeakSensor: PredefinedService; | ||||||
|  |         LightSensor: PredefinedService; | ||||||
|  |         Lightbulb: PredefinedService; | ||||||
|  |         LockManagement: PredefinedService; | ||||||
|  |         LockMechanism: PredefinedService; | ||||||
|  |         Microphone: PredefinedService; | ||||||
|  |         MotionSensor: PredefinedService; | ||||||
|  |         OccupancySensor: PredefinedService; | ||||||
|  |         Outlet: PredefinedService; | ||||||
|  |         Pairing: PredefinedService; | ||||||
|  |         ProtocolInformation: PredefinedService; | ||||||
|  |         Relay: PredefinedService; | ||||||
|  |         SecuritySystem: PredefinedService; | ||||||
|  |         ServiceLabel: PredefinedService; | ||||||
|  |         Slat: PredefinedService; | ||||||
|  |         SmokeSensor: PredefinedService; | ||||||
|  |         Speaker: PredefinedService; | ||||||
|  |         StatefulProgrammableSwitch: PredefinedService; | ||||||
|  |         StatelessProgrammableSwitch: PredefinedService; | ||||||
|  |         Switch: PredefinedService; | ||||||
|  |         Television: PredefinedService; | ||||||
|  |         TelevisionSpeaker: PredefinedService; | ||||||
|  |         TemperatureSensor: PredefinedService; | ||||||
|  |         Thermostat: PredefinedService; | ||||||
|  |         TimeInformation: PredefinedService; | ||||||
|  |         TunneledBTLEAccessoryService: PredefinedService; | ||||||
|  |         Valve: PredefinedService; | ||||||
|  |         Window: PredefinedService; | ||||||
|  |         WindowCovering: PredefinedService; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     export interface PredefinedService { | ||||||
|  |         new(displayName: string, subtype: string): Service; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     export interface CameraSource { | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     type EventAccessory = "service-configurationChange" | "service-characteristic-change" | "identify" | ||||||
|  |  | ||||||
|  |     export interface IEventEmitterAccessory { | ||||||
|  |         addListener(event: EventAccessory, listener: Function): this; | ||||||
|  |         on(event: EventAccessory, listener: Function): this; | ||||||
|  |         once(event: EventAccessory, listener: Function): this; | ||||||
|  |         removeListener(event: EventAccessory, listener: Function): this; | ||||||
|  |         removeAllListeners(event?: EventAccessory): this; | ||||||
|  |         setMaxListeners(n: number): this; | ||||||
|  |         getMaxListeners(): number; | ||||||
|  |         listeners(event: EventAccessory): Function[]; | ||||||
|  |         emit(event: EventAccessory, ...args: any[]): boolean; | ||||||
|  |         listenerCount(type: string): number; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     export interface CharacteristicProps { | ||||||
|  |         format: Characteristic.Formats; | ||||||
|  |         unit: Characteristic.Units, | ||||||
|  |         minValue: number, | ||||||
|  |         maxValue: number, | ||||||
|  |         minStep: number, | ||||||
|  |         perms: Characteristic.Perms[] | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     type EventCharacteristic = "get" | "set" | ||||||
|  |     type CharacteristicValue = boolean | string | number | ||||||
|  |  | ||||||
|  |     export type CharacteristicGetCallback<T = CharacteristicValue> = (error: Error | null, value: T) => void | ||||||
|  |     export type CharacteristicSetCallback = (error?: Error | null) => void | ||||||
|  |     export type CharacteristicCallback = CharacteristicGetCallback | CharacteristicSetCallback | ||||||
|  |  | ||||||
|  |     export interface IEventEmitterCharacteristic { | ||||||
|  |         addListener(event: EventCharacteristic, listener: CharacteristicCallback): this; | ||||||
|  |         on(event: EventCharacteristic, listener: CharacteristicCallback): this; | ||||||
|  |         once(event: EventCharacteristic, listener: CharacteristicCallback): this; | ||||||
|  |         removeListener(event: EventCharacteristic, listener: CharacteristicCallback): this; | ||||||
|  |         removeAllListeners(event?: EventCharacteristic): this; | ||||||
|  |         setMaxListeners(n: number): this; | ||||||
|  |         getMaxListeners(): number; | ||||||
|  |         listeners(event: EventCharacteristic): CharacteristicCallback[]; | ||||||
|  |         emit(event: EventCharacteristic, ...args: any[]): boolean; | ||||||
|  |         listenerCount(type: string): number; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     export interface Characteristic extends IEventEmitterCharacteristic { | ||||||
|  |         new(displayName: string, UUID: string, props?: CharacteristicProps): Characteristic; | ||||||
|  |  | ||||||
|  |         Formats: typeof Characteristic.Formats; | ||||||
|  |         Units: typeof Characteristic.Units; | ||||||
|  |         Perms: typeof Characteristic.Perms; | ||||||
|  |  | ||||||
|  |         setProps(props: CharacteristicProps): Characteristic | ||||||
|  |         getValue(callback?: CharacteristicGetCallback, context?: any, connectionID?: string): void; | ||||||
|  |         setValue(newValue: CharacteristicValue, callback?: CharacteristicSetCallback, context?: any, connectionID?: string): Characteristic; | ||||||
|  |         updateValue(newValue: CharacteristicValue, callback?: () => void, context?: any): Characteristic; | ||||||
|  |         getDefaultValue(): CharacteristicValue; | ||||||
|  |         toHAP(opt: any): JSON; | ||||||
|  |  | ||||||
|  |         AccessoryFlags: Characteristic; | ||||||
|  |         AccessoryIdentifier: Characteristic; | ||||||
|  |         Active: Characteristic; | ||||||
|  |         ActiveIdentifier: Characteristic; | ||||||
|  |         AdministratorOnlyAccess: Characteristic; | ||||||
|  |         AirParticulateDensity: Characteristic; | ||||||
|  |         AirParticulateSize: Characteristic; | ||||||
|  |         AirQuality: Characteristic; | ||||||
|  |         AppMatchingIdentifier: Characteristic; | ||||||
|  |         AudioFeedback: Characteristic; | ||||||
|  |         BatteryLevel: Characteristic; | ||||||
|  |         Brightness: Characteristic; | ||||||
|  |         CarbonDioxideDetected: Characteristic; | ||||||
|  |         CarbonDioxideLevel: Characteristic; | ||||||
|  |         CarbonDioxidePeakLevel: Characteristic; | ||||||
|  |         CarbonMonoxideDetected: Characteristic; | ||||||
|  |         CarbonMonoxideLevel: Characteristic; | ||||||
|  |         CarbonMonoxidePeakLevel: Characteristic; | ||||||
|  |         Category: Characteristic; | ||||||
|  |         ChargingState: Characteristic; | ||||||
|  |         ClosedCaptions: Characteristic; | ||||||
|  |         ColorTemperature: Characteristic; | ||||||
|  |         ConfigureBridgedAccessory: Characteristic; | ||||||
|  |         ConfigureBridgedAccessoryStatus: Characteristic; | ||||||
|  |         ConfiguredName: Characteristic; | ||||||
|  |         ContactSensorState: Characteristic; | ||||||
|  |         CoolingThresholdTemperature: Characteristic; | ||||||
|  |         CurrentAirPurifierState: Characteristic; | ||||||
|  |         CurrentAmbientLightLevel: Characteristic; | ||||||
|  |         CurrentDoorState: Characteristic; | ||||||
|  |         CurrentFanState: Characteristic; | ||||||
|  |         CurrentHeaterCoolerState: Characteristic; | ||||||
|  |         CurrentHeatingCoolingState: Characteristic; | ||||||
|  |         CurrentHorizontalTiltAngle: Characteristic; | ||||||
|  |         CurrentHumidifierDehumidifierState: Characteristic; | ||||||
|  |         CurrentMediaState: Characteristic; | ||||||
|  |         CurrentPosition: Characteristic; | ||||||
|  |         CurrentRelativeHumidity: Characteristic; | ||||||
|  |         CurrentSlatState: Characteristic; | ||||||
|  |         CurrentTemperature: Characteristic; | ||||||
|  |         CurrentTiltAngle: Characteristic; | ||||||
|  |         CurrentTime: Characteristic; | ||||||
|  |         CurrentVerticalTiltAngle: Characteristic; | ||||||
|  |         CurrentVisibilityState: Characteristic; | ||||||
|  |         DayoftheWeek: Characteristic; | ||||||
|  |         DigitalZoom: Characteristic; | ||||||
|  |         DiscoverBridgedAccessories: Characteristic; | ||||||
|  |         DiscoveredBridgedAccessories: Characteristic; | ||||||
|  |         DisplayOrder: Characteristic; | ||||||
|  |         FilterChangeIndication: Characteristic; | ||||||
|  |         FilterLifeLevel: Characteristic; | ||||||
|  |         FirmwareRevision: Characteristic; | ||||||
|  |         HardwareRevision: Characteristic; | ||||||
|  |         HeatingThresholdTemperature: Characteristic; | ||||||
|  |         HoldPosition: Characteristic; | ||||||
|  |         Hue: Characteristic; | ||||||
|  |         Identifier: Characteristic; | ||||||
|  |         Identify: Characteristic; | ||||||
|  |         ImageMirroring: Characteristic; | ||||||
|  |         ImageRotation: Characteristic; | ||||||
|  |         InUse: Characteristic; | ||||||
|  |         InputDeviceType: Characteristic; | ||||||
|  |         InputSourceType: Characteristic; | ||||||
|  |         IsConfigured: Characteristic; | ||||||
|  |         LeakDetected: Characteristic; | ||||||
|  |         LinkQuality: Characteristic; | ||||||
|  |         LockControlPoint: Characteristic; | ||||||
|  |         LockCurrentState: Characteristic; | ||||||
|  |         LockLastKnownAction: Characteristic; | ||||||
|  |         LockManagementAutoSecurityTimeout: Characteristic; | ||||||
|  |         LockPhysicalControls: Characteristic; | ||||||
|  |         LockTargetState: Characteristic; | ||||||
|  |         Logs: Characteristic; | ||||||
|  |         Manufacturer: Characteristic; | ||||||
|  |         Model: Characteristic; | ||||||
|  |         MotionDetected: Characteristic; | ||||||
|  |         Mute: Characteristic; | ||||||
|  |         Name: Characteristic; | ||||||
|  |         NightVision: Characteristic; | ||||||
|  |         NitrogenDioxideDensity: Characteristic; | ||||||
|  |         ObstructionDetected: Characteristic; | ||||||
|  |         OccupancyDetected: Characteristic; | ||||||
|  |         On: Characteristic; | ||||||
|  |         OpticalZoom: Characteristic; | ||||||
|  |         OutletInUse: Characteristic; | ||||||
|  |         OzoneDensity: Characteristic; | ||||||
|  |         PM10Density: Characteristic; | ||||||
|  |         PM2_5Density: Characteristic; | ||||||
|  |         PairSetup: Characteristic; | ||||||
|  |         PairVerify: Characteristic; | ||||||
|  |         PairingFeatures: Characteristic; | ||||||
|  |         PairingPairings: Characteristic; | ||||||
|  |         PictureMode: Characteristic; | ||||||
|  |         PositionState: Characteristic; | ||||||
|  |         PowerModeSelection: Characteristic; | ||||||
|  |         ProgramMode: Characteristic; | ||||||
|  |         ProgrammableSwitchEvent: Characteristic; | ||||||
|  |         ProgrammableSwitchOutputState: Characteristic; | ||||||
|  |         Reachable: Characteristic; | ||||||
|  |         RelativeHumidityDehumidifierThreshold: Characteristic; | ||||||
|  |         RelativeHumidityHumidifierThreshold: Characteristic; | ||||||
|  |         RelayControlPoint: Characteristic; | ||||||
|  |         RelayEnabled: Characteristic; | ||||||
|  |         RelayState: Characteristic; | ||||||
|  |         RemainingDuration: Characteristic; | ||||||
|  |         RemoteKey: Characteristic; | ||||||
|  |         ResetFilterIndication: Characteristic; | ||||||
|  |         RotationDirection: Characteristic; | ||||||
|  |         RotationSpeed: Characteristic; | ||||||
|  |         Saturation: Characteristic; | ||||||
|  |         SecuritySystemAlarmType: Characteristic; | ||||||
|  |         SecuritySystemCurrentState: Characteristic; | ||||||
|  |         SecuritySystemTargetState: Characteristic; | ||||||
|  |         SelectedRTPStreamConfiguration: Characteristic; | ||||||
|  |         SerialNumber: Characteristic; | ||||||
|  |         ServiceLabelIndex: Characteristic; | ||||||
|  |         ServiceLabelNamespace: Characteristic; | ||||||
|  |         SetDuration: Characteristic; | ||||||
|  |         SetupEndpoints: Characteristic; | ||||||
|  |         SlatType: Characteristic; | ||||||
|  |         SleepDiscoveryMode: Characteristic; | ||||||
|  |         SmokeDetected: Characteristic; | ||||||
|  |         SoftwareRevision: Characteristic; | ||||||
|  |         StatusActive: Characteristic; | ||||||
|  |         StatusFault: Characteristic; | ||||||
|  |         StatusJammed: Characteristic; | ||||||
|  |         StatusLowBattery: Characteristic; | ||||||
|  |         StatusTampered: Characteristic; | ||||||
|  |         StreamingStatus: Characteristic; | ||||||
|  |         SulphurDioxideDensity: Characteristic; | ||||||
|  |         SupportedAudioStreamConfiguration: Characteristic; | ||||||
|  |         SupportedRTPConfiguration: Characteristic; | ||||||
|  |         SupportedVideoStreamConfiguration: Characteristic; | ||||||
|  |         SwingMode: Characteristic; | ||||||
|  |         TargetAirPurifierState: Characteristic; | ||||||
|  |         TargetAirQuality: Characteristic; | ||||||
|  |         TargetDoorState: Characteristic; | ||||||
|  |         TargetFanState: Characteristic; | ||||||
|  |         TargetHeaterCoolerState: Characteristic; | ||||||
|  |         TargetHeatingCoolingState: Characteristic; | ||||||
|  |         TargetHorizontalTiltAngle: Characteristic; | ||||||
|  |         TargetHumidifierDehumidifierState: Characteristic; | ||||||
|  |         TargetMediaState: Characteristic; | ||||||
|  |         TargetPosition: Characteristic; | ||||||
|  |         TargetRelativeHumidity: Characteristic; | ||||||
|  |         TargetSlatState: Characteristic; | ||||||
|  |         TargetTemperature: Characteristic; | ||||||
|  |         TargetTiltAngle: Characteristic; | ||||||
|  |         TargetVerticalTiltAngle: Characteristic; | ||||||
|  |         TargetVisibilityState: Characteristic; | ||||||
|  |         TemperatureDisplayUnits: Characteristic; | ||||||
|  |         TimeUpdate: Characteristic; | ||||||
|  |         TunnelConnectionTimeout: Characteristic; | ||||||
|  |         TunneledAccessoryAdvertising: Characteristic; | ||||||
|  |         TunneledAccessoryConnected: Characteristic; | ||||||
|  |         TunneledAccessoryStateNumber: Characteristic; | ||||||
|  |         VOCDensity: Characteristic; | ||||||
|  |         ValveType: Characteristic; | ||||||
|  |         Version: Characteristic; | ||||||
|  |         Volume: Characteristic; | ||||||
|  |         VolumeControlType: Characteristic; | ||||||
|  |         VolumeSelector: Characteristic; | ||||||
|  |         WaterLevel: Characteristic; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     module Characteristic { | ||||||
|  |         export enum Formats { | ||||||
|  |             BOOL, | ||||||
|  |             INT, | ||||||
|  |             FLOAT, | ||||||
|  |             STRING, | ||||||
|  |             ARRAY, // unconfirmed | ||||||
|  |             DICTIONARY, // unconfirmed | ||||||
|  |             UINT8, | ||||||
|  |             UINT16, | ||||||
|  |             UINT32, | ||||||
|  |             UINT64, | ||||||
|  |             DATA, // unconfirmed | ||||||
|  |             TLV8 | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         export enum Units { | ||||||
|  |             // HomeKit only defines Celsius, for Fahrenheit, it requires iOS app to do the conversion. | ||||||
|  |             CELSIUS, | ||||||
|  |             PERCENTAGE, | ||||||
|  |             ARC_DEGREE, | ||||||
|  |             LUX, | ||||||
|  |             SECONDS | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         export enum Perms { | ||||||
|  |             READ, | ||||||
|  |             WRITE, | ||||||
|  |             NOTIFY, | ||||||
|  |             HIDDEN | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     export interface PublishInfo { | ||||||
|  |         port: number; | ||||||
|  |         username: string; | ||||||
|  |         pincode: string; | ||||||
|  |         category: number; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     export interface Accessory extends IEventEmitterAccessory { | ||||||
|  |         new(displayName: string, UUID: string): Accessory; | ||||||
|  |         displayName: string; | ||||||
|  |         username: string; | ||||||
|  |         pincode: string; | ||||||
|  |         UUID: string; | ||||||
|  |         aid: string; | ||||||
|  |         bridged: boolean; | ||||||
|  |         bridgedAccessories: Accessory[]; | ||||||
|  |         reachable: boolean; | ||||||
|  |         category: Accessory.Categories; | ||||||
|  |         services: Service[]; | ||||||
|  |         cameraSource: CameraSource; | ||||||
|  |         Categories: typeof Accessory.Categories | ||||||
|  |         addService(service: Service | Function): Service; | ||||||
|  |         removeService(service: Service): void; | ||||||
|  |         getService(name: string | Function): Service; | ||||||
|  |         updateReachability(reachable: boolean): void; | ||||||
|  |         addBridgedAccessory(accessory: Accessory, deferUpdate: boolean): Accessory; | ||||||
|  |         addBridgedAccessories(accessories: Accessory[]): void | ||||||
|  |         removeBridgedAccessory(accessory: Accessory, deferUpdate: boolean): void; | ||||||
|  |         removeBridgedAccessories(accessories: Accessory[]): void; | ||||||
|  |         getCharacteristicByIID(iid: string): Characteristic; | ||||||
|  |         getBridgedAccessoryByAID(aid: string): Accessory; | ||||||
|  |         findCharacteristic(aid: string, iid: string): Accessory; | ||||||
|  |         configureCameraSource(cameraSource: CameraSource): void; | ||||||
|  |         toHAP(opt: any): JSON; | ||||||
|  |         publish(info: PublishInfo, allowInsecureRequest: boolean): void; | ||||||
|  |         destroy(): void; | ||||||
|  |         setupURI(): string; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     module Accessory { | ||||||
|  |         export enum Categories { | ||||||
|  |             OTHER = 1, | ||||||
|  |             BRIDGE = 2, | ||||||
|  |             FAN = 3, | ||||||
|  |             GARAGE_DOOR_OPENER = 4, | ||||||
|  |             LIGHTBULB = 5, | ||||||
|  |             DOOR_LOCK = 6, | ||||||
|  |             OUTLET = 7, | ||||||
|  |             SWITCH = 8, | ||||||
|  |             THERMOSTAT = 9, | ||||||
|  |             SENSOR = 10, | ||||||
|  |             ALARM_SYSTEM = 11, | ||||||
|  |             SECURITY_SYSTEM = 11, | ||||||
|  |             DOOR = 12, | ||||||
|  |             WINDOW = 13, | ||||||
|  |             WINDOW_COVERING = 14, | ||||||
|  |             PROGRAMMABLE_SWITCH = 15, | ||||||
|  |             RANGE_EXTENDER = 16, | ||||||
|  |             CAMERA = 17, | ||||||
|  |             IP_CAMERA = 17, | ||||||
|  |             VIDEO_DOORBELL = 18, | ||||||
|  |             AIR_PURIFIER = 19, | ||||||
|  |             AIR_HEATER = 20, | ||||||
|  |             AIR_CONDITIONER = 21, | ||||||
|  |             AIR_HUMIDIFIER = 22, | ||||||
|  |             AIR_DEHUMIDIFIER = 23, | ||||||
|  |             APPLE_TV = 24, | ||||||
|  |             SPEAKER = 26, | ||||||
|  |             AIRPORT = 27, | ||||||
|  |             SPRINKLER = 28, | ||||||
|  |             FAUCET = 29, | ||||||
|  |             SHOWER_HEAD = 30, | ||||||
|  |             TELEVISION = 31, | ||||||
|  |             TARGET_CONTROLLER = 32 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     export interface HAPNodeJS { | ||||||
|  |         init(storagePath?: string): void, | ||||||
|  |         uuid: uuid, | ||||||
|  |         Accessory: Accessory, | ||||||
|  |         Service: Service, | ||||||
|  |         Characteristic: Characteristic | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | declare var hapNodeJS: HAPNodeJS.HAPNodeJS; | ||||||
|  |  | ||||||
|  | declare module "hap-nodejs" { | ||||||
|  |     export = hapNodeJS; | ||||||
|  | } | ||||||
							
								
								
									
										30
									
								
								src/Util/Callbackify.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/Util/Callbackify.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Helper function to convert callbacks into promises | ||||||
|  |  * @param func  | ||||||
|  |  */ | ||||||
|  | export default function callbackify(func: (...args: any[]) => Promise<any>): Function { | ||||||
|  |     return (...args: any[]) => { | ||||||
|  |         const onlyArgs: any[] = []; | ||||||
|  |         let maybeCallback: Function | null = null; | ||||||
|  |  | ||||||
|  |         for (const arg of args) { | ||||||
|  |             if (typeof arg === 'function') { | ||||||
|  |                 maybeCallback = arg; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             onlyArgs.push(arg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!maybeCallback) { | ||||||
|  |             throw new Error("Missing callback parameter!"); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         const callback = maybeCallback; | ||||||
|  |  | ||||||
|  |         func(...onlyArgs) | ||||||
|  |             .then((data: any) => callback(null, data)) | ||||||
|  |             .catch((err: any) => callback(err)) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,290 +0,0 @@ | |||||||
| import { PlatformAccessory, Service } from "homebridge"; |  | ||||||
| import { IActivity } from "../models/config"; |  | ||||||
| import { Platform } from "../platform"; |  | ||||||
| import { ActivityService } from "../services/activityService"; |  | ||||||
| import { CommandService } from "../services/commandService"; |  | ||||||
| import { VolumeService } from "../services/volumeService"; |  | ||||||
| import callbackify from "../util/callbackify"; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * 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 class ControlUnit { |  | ||||||
|   constructor( |  | ||||||
|     private readonly _platform: Platform, |  | ||||||
|     private readonly _accessory: PlatformAccessory, |  | ||||||
|     private _activityService: ActivityService, |  | ||||||
|     private _commandService: CommandService, |  | ||||||
|     private _volumeService: VolumeService, |  | ||||||
|     private _activities: Array<IActivity> |  | ||||||
|   ) { |  | ||||||
|     this._accessory |  | ||||||
|       .getService(this._platform.Service.AccessoryInformation)! |  | ||||||
|       .setCharacteristic( |  | ||||||
|         this._platform.Characteristic.Manufacturer, |  | ||||||
|         "Brandon Watson" |  | ||||||
|       ) |  | ||||||
|       .setCharacteristic( |  | ||||||
|         this._platform.Characteristic.Model, |  | ||||||
|         "Matrix Output Television" |  | ||||||
|       ) |  | ||||||
|       .setCharacteristic( |  | ||||||
|         this._platform.Characteristic.SerialNumber, |  | ||||||
|         "123-456-789" |  | ||||||
|       ); |  | ||||||
|  |  | ||||||
|     const televisionUUID = this._platform.api.hap.uuid.generate( |  | ||||||
|       `${this._accessory.displayName} Television` |  | ||||||
|     ); |  | ||||||
|     //Init services |  | ||||||
|     const televisionService = |  | ||||||
|       this._accessory.getService(this._platform.Service.Television) || |  | ||||||
|       this._accessory.addService( |  | ||||||
|         this._platform.Service.Television, |  | ||||||
|         "Television", |  | ||||||
|         televisionUUID |  | ||||||
|       ); |  | ||||||
|     this.configureTvService(televisionService); |  | ||||||
|  |  | ||||||
|     const televisionSpeakerUUID = this._platform.api.hap.uuid.generate( |  | ||||||
|       `${this._accessory.displayName} Television Speaker` |  | ||||||
|     ); |  | ||||||
|     const televisionSpeakerService = |  | ||||||
|       this._accessory.getService(this._platform.Service.TelevisionSpeaker) || |  | ||||||
|       this._accessory.addService( |  | ||||||
|         this._platform.Service.TelevisionSpeaker, |  | ||||||
|         "Television Speaker", |  | ||||||
|         televisionSpeakerUUID |  | ||||||
|       ); |  | ||||||
|     this.configureTvSpeakerService(televisionService, televisionSpeakerService); |  | ||||||
|  |  | ||||||
|     this.configureInputSourceService(televisionService); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /************* |  | ||||||
|    * |  | ||||||
|    * Tv Service |  | ||||||
|    * |  | ||||||
|    *************/ |  | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * Configure television service |  | ||||||
|    */ |  | ||||||
|   private configureTvService(televisionService: Service): void { |  | ||||||
|     televisionService.setCharacteristic( |  | ||||||
|       this._platform.Characteristic.ConfiguredName, |  | ||||||
|       this._accessory.displayName |  | ||||||
|     ); |  | ||||||
|     //@ts-ignore |  | ||||||
|     televisionService.setCharacteristic( |  | ||||||
|       this._platform.Characteristic.SleepDiscoveryMode, |  | ||||||
|       this._platform.Characteristic.SleepDiscoveryMode.ALWAYS_DISCOVERABLE |  | ||||||
|     ); |  | ||||||
|     televisionService.setCharacteristic( |  | ||||||
|       this._platform.Characteristic.ActiveIdentifier, |  | ||||||
|       1 |  | ||||||
|     ); |  | ||||||
|     televisionService.setCharacteristic( |  | ||||||
|       this._platform.Characteristic.Active, |  | ||||||
|       false |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     //setup listeners |  | ||||||
|     televisionService |  | ||||||
|       .getCharacteristic(this._platform.Characteristic.Active) |  | ||||||
|       //@ts-ignore |  | ||||||
|       .on("set", callbackify(this.onSetAccessoryActive)) |  | ||||||
|       //@ts-ignore |  | ||||||
|       .on("get", callbackify(this.onGetAccessoryActive)); |  | ||||||
|  |  | ||||||
|     //Set remote characteristics if is external |  | ||||||
|     televisionService |  | ||||||
|       .getCharacteristic(this._platform.Characteristic.RemoteKey) |  | ||||||
|       //@ts-ignore |  | ||||||
|       .on("set", callbackify(this.onSetRemoteKey)); |  | ||||||
|  |  | ||||||
|     televisionService |  | ||||||
|       .getCharacteristic(this._platform.Characteristic.ActiveIdentifier) |  | ||||||
|       //@ts-ignore |  | ||||||
|       .on("set", callbackify(this.onSetActiveIdentifier)) |  | ||||||
|       //@ts-ignore |  | ||||||
|       .on("get", callbackify(this.onGetActiveIdentifier)); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * Event handler for SET active characteristic |  | ||||||
|    */ |  | ||||||
|   private onSetAccessoryActive = async (value: any) => { |  | ||||||
|     switch (value) { |  | ||||||
|       case 0: |  | ||||||
|         this._activityService.stopCurrentActivity(this._accessory.displayName); |  | ||||||
|         break; |  | ||||||
|       //Turn on with first activity |  | ||||||
|       case 1: |  | ||||||
|         this._activityService.startActivity( |  | ||||||
|           this._accessory.displayName, |  | ||||||
|           this._activities[0] |  | ||||||
|         ); |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * Event handler for GET active characteristic |  | ||||||
|    */ |  | ||||||
|   private onGetAccessoryActive = async () => { |  | ||||||
|     //@ts-ignore |  | ||||||
|     return this._activityService.getIsActive(this._accessory.displayName) |  | ||||||
|       ? this._platform.Characteristic.Active.ACTIVE |  | ||||||
|       : this._platform.Characteristic.Active.INACTIVE; |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * Event handler for SET remote key |  | ||||||
|    */ |  | ||||||
|   private onSetRemoteKey = async (key: any) => { |  | ||||||
|     this._commandService.sendKeyPress(this._accessory.displayName, key); |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * Event handler for SET active identifier characteristic |  | ||||||
|    */ |  | ||||||
|   private onSetActiveIdentifier = async (identifier: any) => { |  | ||||||
|     this._activityService.startActivity( |  | ||||||
|       this._accessory.displayName, |  | ||||||
|       this._activities[identifier] |  | ||||||
|     ); |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * Event handler for GET active identifier characteristic |  | ||||||
|    */ |  | ||||||
|   private onGetActiveIdentifier = async () => { |  | ||||||
|     let currentActivity: IActivity = this._activityService.getIsActive( |  | ||||||
|       this._accessory.displayName |  | ||||||
|     )!; |  | ||||||
|     let identifier: number = 0; |  | ||||||
|     if (currentActivity) { |  | ||||||
|       identifier = this._activities.findIndex( |  | ||||||
|         (e) => e.DisplayName === currentActivity.DisplayName |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
|     return identifier; |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   /****************** |  | ||||||
|    * |  | ||||||
|    * Speaker Service |  | ||||||
|    * |  | ||||||
|    *****************/ |  | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * Configure Speaker Service |  | ||||||
|    */ |  | ||||||
|   private configureTvSpeakerService( |  | ||||||
|     televisionService: Service, |  | ||||||
|     televisionSpeakerService: Service |  | ||||||
|   ): void { |  | ||||||
|     televisionSpeakerService.setCharacteristic( |  | ||||||
|       this._platform.Characteristic.Name, |  | ||||||
|       this._accessory.displayName |  | ||||||
|     ); |  | ||||||
|     //@ts-ignore |  | ||||||
|     televisionSpeakerService.setCharacteristic( |  | ||||||
|       this._platform.Characteristic.Active, |  | ||||||
|       this._platform.Characteristic.Active.ACTIVE |  | ||||||
|     ); |  | ||||||
|     //@ts-ignore |  | ||||||
|     televisionSpeakerService.setCharacteristic( |  | ||||||
|       this._platform.Characteristic.VolumeControlType, |  | ||||||
|       this._platform.Characteristic.VolumeControlType.ABSOLUTE |  | ||||||
|     ); |  | ||||||
|     televisionSpeakerService.subtype = this._accessory.displayName + "Volume"; |  | ||||||
|     if (televisionService) { |  | ||||||
|       //@ts-ignore |  | ||||||
|       televisionService.addLinkedService(televisionSpeakerService); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     //Setup listeners |  | ||||||
|     televisionSpeakerService |  | ||||||
|       .getCharacteristic(this._platform.Characteristic.VolumeSelector) |  | ||||||
|       //@ts-ignore |  | ||||||
|       .on("set", callbackify(this.onSetVolumeSelector)); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * Event handler for SET volume characteristic |  | ||||||
|    */ |  | ||||||
|   private onSetVolumeSelector = async (value: any) => { |  | ||||||
|     switch (value) { |  | ||||||
|       case 0: |  | ||||||
|         this._volumeService.volumeUp(this._accessory.displayName); |  | ||||||
|         break; |  | ||||||
|       case 1: |  | ||||||
|         this._volumeService.volumeDown(this._accessory.displayName); |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   /***************** |  | ||||||
|    * |  | ||||||
|    * Input services |  | ||||||
|    * |  | ||||||
|    *****************/ |  | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * Configure input service |  | ||||||
|    */ |  | ||||||
|   private configureInputSourceService(televisionService: Service): void { |  | ||||||
|     let inputs: Array<Service> = []; |  | ||||||
|     this._activities.forEach((activity: IActivity, index: number) => { |  | ||||||
|       const inputUUID = this._platform.api.hap.uuid.generate( |  | ||||||
|         `${activity.DisplayName} Input` |  | ||||||
|       ); |  | ||||||
|       let inputService = |  | ||||||
|         this._accessory.getService(activity.DisplayName) || |  | ||||||
|         this._accessory.addService( |  | ||||||
|           this._platform.Service.InputSource, |  | ||||||
|           activity.DisplayName, |  | ||||||
|           inputUUID |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|       inputService |  | ||||||
|         .setCharacteristic(this._platform.Characteristic.Identifier, index) |  | ||||||
|         .setCharacteristic( |  | ||||||
|           this._platform.Characteristic.ConfiguredName, |  | ||||||
|           activity.DisplayName |  | ||||||
|         ) |  | ||||||
|         .setCharacteristic( |  | ||||||
|           this._platform.Characteristic.IsConfigured, |  | ||||||
|           //@ts-ignore |  | ||||||
|           this._platform.Characteristic.IsConfigured.CONFIGURED |  | ||||||
|         ) |  | ||||||
|         //@ts-ignore |  | ||||||
|         .setCharacteristic( |  | ||||||
|           this._platform.Characteristic.InputSourceType, |  | ||||||
|           this._platform.Characteristic.InputSourceType.HDMI |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|       //@ts-ignore |  | ||||||
|       televisionService!.addLinkedService(inputService); |  | ||||||
|       inputs.push(inputService); |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -1,122 +0,0 @@ | |||||||
| import { PlatformAccessory, Service } from "homebridge"; |  | ||||||
| import { HarmonyDataProvider } from "../dataProviders/harmonyDataProvider"; |  | ||||||
| import { IDeviceButton } from "../models/config"; |  | ||||||
| import { HarmonyDevice } from "../models/harmonyDevice"; |  | ||||||
| import { Platform } from "../platform"; |  | ||||||
|  |  | ||||||
| export class DeviceButton { |  | ||||||
|   private _buttonState: boolean; |  | ||||||
|   private _device!: HarmonyDevice; |  | ||||||
|   private _switchService: Service; |  | ||||||
|  |  | ||||||
|   constructor( |  | ||||||
|     private readonly _platform: Platform, |  | ||||||
|     private readonly _accessory: PlatformAccessory, |  | ||||||
|     private _dataProvider: HarmonyDataProvider, |  | ||||||
|     private _deviceInfo: IDeviceButton |  | ||||||
|   ) { |  | ||||||
|     this._buttonState = false; |  | ||||||
|  |  | ||||||
|     if (this._deviceInfo.NumberOfKeyPresses && this._deviceInfo.IsStateful) { |  | ||||||
|       throw new Error( |  | ||||||
|         "A button cannot be stateful and be pressed more than once" |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     this._accessory |  | ||||||
|       .getService(this._platform.Service.AccessoryInformation)! |  | ||||||
|       .setCharacteristic( |  | ||||||
|         this._platform.Characteristic.Manufacturer, |  | ||||||
|         "Brandon Watson" |  | ||||||
|       ) |  | ||||||
|       .setCharacteristic(this._platform.Characteristic.Model, "Device Button") |  | ||||||
|       .setCharacteristic( |  | ||||||
|         this._platform.Characteristic.SerialNumber, |  | ||||||
|         "123-456-789" |  | ||||||
|       ); |  | ||||||
|  |  | ||||||
|     const switchUUID = this._platform.api.hap.uuid.generate( |  | ||||||
|       `${this._accessory.displayName} Switch` |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     this._switchService = |  | ||||||
|       this._accessory.getService(this._platform.Service.Switch) || |  | ||||||
|       this._accessory.addService( |  | ||||||
|         this._platform.Service.Switch, |  | ||||||
|         this._accessory.displayName, |  | ||||||
|         switchUUID |  | ||||||
|       ); |  | ||||||
|  |  | ||||||
|     this._switchService |  | ||||||
|       .getCharacteristic(this._platform.Characteristic.On) |  | ||||||
|       //@ts-ignore |  | ||||||
|       .on("set", this.onSwitchSet) |  | ||||||
|       .updateValue(this._buttonState) |  | ||||||
|       .on("get", this.onSwitchGet); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * Handler for switch set event |  | ||||||
|    * @param callback The callback function to call when complete |  | ||||||
|    */ |  | ||||||
|   private onSwitchSet = async ( |  | ||||||
|     newState: boolean, |  | ||||||
|     callback: (error?: Error | null | undefined) => void |  | ||||||
|   ) => { |  | ||||||
|     if (!this._deviceInfo.IsStateful && newState === this._buttonState) { |  | ||||||
|       return callback(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     //Get device command if we don't have it |  | ||||||
|     if (!this._device) { |  | ||||||
|       this._device = this._dataProvider.getDeviceByName( |  | ||||||
|         this._deviceInfo.DeviceName |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     //Execute command |  | ||||||
|     if (!this._device) { |  | ||||||
|       return callback(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     //change state if stateful |  | ||||||
|     if (this._deviceInfo.IsStateful && this._buttonState != newState) { |  | ||||||
|       this._buttonState = newState; |  | ||||||
|       await this._dataProvider.sendCommand( |  | ||||||
|         this._deviceInfo.ButtonName, |  | ||||||
|         this._device |  | ||||||
|       ); |  | ||||||
|       return callback(); |  | ||||||
|     } else if (!this._deviceInfo.IsStateful) { |  | ||||||
|       //Send the number of configured key presses |  | ||||||
|       for (let i = 0; i < this._deviceInfo.NumberOfKeyPresses; i++) { |  | ||||||
|         await this._dataProvider.sendCommand( |  | ||||||
|           this._deviceInfo.ButtonName, |  | ||||||
|           this._device |  | ||||||
|         ); |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       this._switchService |  | ||||||
|         .getCharacteristic(this._platform.Characteristic.On) |  | ||||||
|         .updateValue(false); |  | ||||||
|  |  | ||||||
|       this._buttonState = false; |  | ||||||
|       return callback(); |  | ||||||
|     } |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * Handler for switch get event |  | ||||||
|    * @param callback The callback function to call when complete |  | ||||||
|    */ |  | ||||||
|   private onSwitchGet = ( |  | ||||||
|     callback: (error: Error | null, value: boolean) => void |  | ||||||
|   ) => { |  | ||||||
|     //Only return state if button is stateful |  | ||||||
|     if (this._deviceInfo.IsStateful) { |  | ||||||
|       return callback(null, this._buttonState); |  | ||||||
|     } else { |  | ||||||
|       return callback(null, false); |  | ||||||
|     } |  | ||||||
|   }; |  | ||||||
| } |  | ||||||
| @@ -1,2 +0,0 @@ | |||||||
| export { ControlUnit } from "./controlUnit"; |  | ||||||
| export { DeviceButton } from "./deviceButton"; |  | ||||||
| @@ -1,105 +0,0 @@ | |||||||
| import { |  | ||||||
|   CharacteristicGetCallback, |  | ||||||
|   CharacteristicSetCallback, |  | ||||||
|   CharacteristicValue, |  | ||||||
|   PlatformAccessory, |  | ||||||
|   Service, |  | ||||||
| } from "homebridge"; |  | ||||||
| import { HarmonyDataProvider } from "../dataProviders/harmonyDataProvider"; |  | ||||||
| import { ISequence } from "../models/config/sequence"; |  | ||||||
| import { HarmonyDevice } from "../models/harmonyDevice"; |  | ||||||
| import { Platform } from "../platform"; |  | ||||||
| import { sleep } from "../util"; |  | ||||||
|  |  | ||||||
| export class Sequence { |  | ||||||
|   private _devices: { [deviceName: string]: HarmonyDevice }; |  | ||||||
|   private _switchService: Service; |  | ||||||
|  |  | ||||||
|   constructor( |  | ||||||
|     private readonly _platform: Platform, |  | ||||||
|     private readonly _accessory: PlatformAccessory, |  | ||||||
|     private _dataProvider: HarmonyDataProvider, |  | ||||||
|     private _sequence: ISequence |  | ||||||
|   ) { |  | ||||||
|     this._accessory |  | ||||||
|       .getService(this._platform.Service.AccessoryInformation)! |  | ||||||
|       .setCharacteristic( |  | ||||||
|         this._platform.Characteristic.Manufacturer, |  | ||||||
|         "Brandon Watson" |  | ||||||
|       ) |  | ||||||
|       .setCharacteristic(this._platform.Characteristic.Model, "Sequence Button") |  | ||||||
|       .setCharacteristic( |  | ||||||
|         this._platform.Characteristic.SerialNumber, |  | ||||||
|         "123-456-789" |  | ||||||
|       ); |  | ||||||
|  |  | ||||||
|     const switchUUID = this._platform.api.hap.uuid.generate( |  | ||||||
|       `${this._accessory.displayName} Switch` |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     this._switchService = |  | ||||||
|       this._accessory.getService(this._platform.Service.Switch) || |  | ||||||
|       this._accessory.addService( |  | ||||||
|         this._platform.Service.Switch, |  | ||||||
|         this._accessory.displayName, |  | ||||||
|         switchUUID |  | ||||||
|       ); |  | ||||||
|  |  | ||||||
|     this._switchService |  | ||||||
|       .getCharacteristic(this._platform.Characteristic.On) |  | ||||||
|       .on("set", this.onSwitchSet) |  | ||||||
|       .updateValue(false) |  | ||||||
|       .on("get", (callback: CharacteristicGetCallback): void => { |  | ||||||
|         return callback(null); |  | ||||||
|       }); |  | ||||||
|  |  | ||||||
|     this._devices = {}; |  | ||||||
|     // Get devices in sequence |  | ||||||
|     for (const deviceName of _sequence.Steps.map((e) => e.DeviceName)) { |  | ||||||
|       if (!deviceName) { |  | ||||||
|         continue; |  | ||||||
|       } |  | ||||||
|       const device = this._dataProvider.getDeviceByName(deviceName); |  | ||||||
|       if (device) { |  | ||||||
|         this._devices[deviceName] = device; |  | ||||||
|       } else { |  | ||||||
|         this._platform.log.warn( |  | ||||||
|           `Device ${deviceName} was not found in harmony configuration` |  | ||||||
|         ); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * Handler for switchSet command |  | ||||||
|    * @param callback |  | ||||||
|    */ |  | ||||||
|   public onSwitchSet = async ( |  | ||||||
|     _value: CharacteristicValue, |  | ||||||
|     callback: CharacteristicSetCallback |  | ||||||
|   ): Promise<void> => { |  | ||||||
|     // Execute sequence |  | ||||||
|     for (const step of this._sequence.Steps) { |  | ||||||
|       await sleep(step.Delay); |  | ||||||
|       const device: HarmonyDevice = this._devices[step.DeviceName ?? ""]; |  | ||||||
|       if ( |  | ||||||
|         device && |  | ||||||
|         step.DeviceCommand && |  | ||||||
|         device.supportsCommand(step.DeviceCommand) |  | ||||||
|       ) { |  | ||||||
|         await this._dataProvider.sendCommand(step.DeviceCommand, device); |  | ||||||
|       } else { |  | ||||||
|         this._platform.log.warn( |  | ||||||
|           `Attempted to execute command ${step.DeviceCommand} on device ${step.DeviceName} but the device or command was not found` |  | ||||||
|         ); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Deactivate button |  | ||||||
|     this._switchService |  | ||||||
|       .getCharacteristic(this._platform.Characteristic.On) |  | ||||||
|       .updateValue(false); |  | ||||||
|  |  | ||||||
|     callback(null); |  | ||||||
|   }; |  | ||||||
| } |  | ||||||
| @@ -1,166 +0,0 @@ | |||||||
| import { Logging } from "homebridge"; |  | ||||||
| import { inject, injectable } from "tsyringe"; |  | ||||||
| import { ICommand } from "../models"; |  | ||||||
| import { IConfig } from "../models/config"; |  | ||||||
| import { IDeviceConfig } from "../models/config/deviceConfig"; |  | ||||||
| import { IHub } from "../models/config/hub"; |  | ||||||
| import { HarmonyDevice } from "../models/harmonyDevice"; |  | ||||||
| import { HarmonyHub } from "../models/harmonyHub"; |  | ||||||
|  |  | ||||||
| @injectable() |  | ||||||
| export class HarmonyDataProvider { |  | ||||||
|   private _hubs: { [hubName: string]: HarmonyHub } = {}; |  | ||||||
|   private _deviceConfigByName: { [deviceName: string]: IDeviceConfig } = {}; |  | ||||||
|  |  | ||||||
|   constructor( |  | ||||||
|     @inject("IConfig") private _config: IConfig, |  | ||||||
|     @inject("log") private _log: Logging |  | ||||||
|   ) { |  | ||||||
|     _config.Devices.forEach((deviceConfig: IDeviceConfig) => { |  | ||||||
|       this._deviceConfigByName[deviceConfig.Name] = deviceConfig; |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     this.connect(_config.Hubs); |  | ||||||
|     this.emitInfo(); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   public async powerOnDevices(devices: Array<HarmonyDevice>): Promise<void> { |  | ||||||
|     await Promise.all( |  | ||||||
|       devices.map(async (device: HarmonyDevice) => { |  | ||||||
|         if (device && device.name) { |  | ||||||
|           if (!device.on) { |  | ||||||
|             this._log.info(`Turning on device ${device.name}`); |  | ||||||
|             await this.powerOnDevice(device); |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       }) |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   public async powerOffDevices(devices: Array<HarmonyDevice>) { |  | ||||||
|     await Promise.all( |  | ||||||
|       //Turn off devices |  | ||||||
|       devices.map(async (device: HarmonyDevice) => { |  | ||||||
|         if (device) { |  | ||||||
|           if (device.on) { |  | ||||||
|             this._log.info(`Turning off device ${device.name}`); |  | ||||||
|             await this.powerOffDevice(device); |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       }) |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   public async sendCommand( |  | ||||||
|     commandName: string, |  | ||||||
|     harmonyDevice: HarmonyDevice |  | ||||||
|   ): Promise<void> { |  | ||||||
|     let command!: ICommand; |  | ||||||
|  |  | ||||||
|     commandName = this.getOverrideCommand(commandName, harmonyDevice); |  | ||||||
|  |  | ||||||
|     if (harmonyDevice.supportsCommand(commandName)) { |  | ||||||
|       command = harmonyDevice.getCommand(commandName); |  | ||||||
|     } |  | ||||||
|     const hub = this.getHubByDevice(harmonyDevice); |  | ||||||
|     await hub.sendCommand(command); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * Get the IDevice by name. |  | ||||||
|    * @param deviceName The device to retrieve. |  | ||||||
|    */ |  | ||||||
|   public getDeviceByName(deviceName: string): HarmonyDevice { |  | ||||||
|     let device: HarmonyDevice | undefined; |  | ||||||
|     try { |  | ||||||
|       device = |  | ||||||
|         this._hubs[this._deviceConfigByName[deviceName].Hub].getDeviceByName( |  | ||||||
|           deviceName |  | ||||||
|         ); |  | ||||||
|     } catch (err) { |  | ||||||
|       this._log.info(`Error retrieving device from hub: ${err}`); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return device!; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   private connect = async (hubs: Array<IHub>) => { |  | ||||||
|     let readyCount = 0; |  | ||||||
|     await Promise.all( |  | ||||||
|       hubs.map(async (hub: IHub): Promise<void> => { |  | ||||||
|         const newHarmonyHub = new HarmonyHub(hub.Name, hub.Ip, this._log.info); |  | ||||||
|         this._hubs[hub.Name] = newHarmonyHub; |  | ||||||
|         newHarmonyHub.on("Ready", () => { |  | ||||||
|           readyCount++; |  | ||||||
|           if (readyCount === Object.keys(this._hubs).length) { |  | ||||||
|             // this.emit("Ready"); |  | ||||||
|           } |  | ||||||
|         }); |  | ||||||
|         await newHarmonyHub.initialize(); |  | ||||||
|       }) |  | ||||||
|     ); |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   private emitInfo(): void { |  | ||||||
|     //Emit devices if requested |  | ||||||
|     this._log.info("All hubs connected"); |  | ||||||
|     if (this._config.EmitDevicesOnStartup) { |  | ||||||
|       const hubs = this._hubs; |  | ||||||
|       Object.values(hubs).forEach((hub: HarmonyHub) => { |  | ||||||
|         const deviceDictionary = hub.devices; |  | ||||||
|         this._log.info(`${hub.hubName}`); |  | ||||||
|         Object.values(deviceDictionary).forEach((device: HarmonyDevice) => { |  | ||||||
|           this._log.info(`    ${device.name} : ${device.id}`); |  | ||||||
|           Object.keys(device.commands).forEach((command: string) => { |  | ||||||
|             this._log.info(`        ${command}`); |  | ||||||
|           }); |  | ||||||
|         }); |  | ||||||
|       }); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   private getHubByDevice(device: HarmonyDevice) { |  | ||||||
|     return this._hubs[this._deviceConfigByName[device.name].Hub]; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   private async powerOnDevice(harmonyDevice: HarmonyDevice): Promise<void> { |  | ||||||
|     let powerOnCommand: string = "Power On"; |  | ||||||
|     let powerToggleCommand: string = "Power Toggle"; |  | ||||||
|     if (harmonyDevice.supportsCommand(powerOnCommand)) { |  | ||||||
|       await this.sendCommand(powerOnCommand, harmonyDevice); |  | ||||||
|       harmonyDevice.on = true; |  | ||||||
|     } else if (harmonyDevice.supportsCommand(powerToggleCommand)) { |  | ||||||
|       await this.sendCommand(powerToggleCommand, harmonyDevice); |  | ||||||
|       harmonyDevice.on = true; |  | ||||||
|     } else { |  | ||||||
|       await this.sendCommand(powerOnCommand, harmonyDevice); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   private async powerOffDevice(harmonyDevice: HarmonyDevice): Promise<void> { |  | ||||||
|     let powerOffCommand: string = "Power Off"; |  | ||||||
|     let powerToggleCommand: string = "Power Toggle"; |  | ||||||
|     if (harmonyDevice.supportsCommand(powerOffCommand)) { |  | ||||||
|       await this.sendCommand(powerOffCommand, harmonyDevice); |  | ||||||
|       harmonyDevice.on = false; |  | ||||||
|     } else if (harmonyDevice.supportsCommand(powerToggleCommand)) { |  | ||||||
|       await this.sendCommand(powerToggleCommand, harmonyDevice); |  | ||||||
|       harmonyDevice.on = false; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   private getOverrideCommand( |  | ||||||
|     commandName: string, |  | ||||||
|     harmonyDevice: HarmonyDevice |  | ||||||
|   ) { |  | ||||||
|     const deviceConfig: IDeviceConfig = |  | ||||||
|       this._deviceConfigByName[harmonyDevice.name]; |  | ||||||
|     if (!deviceConfig.Overrides) { |  | ||||||
|       return commandName; |  | ||||||
|     } |  | ||||||
|     const overrideCommand = deviceConfig.Overrides.find( |  | ||||||
|       (e) => e.Command == commandName |  | ||||||
|     ); |  | ||||||
|     return overrideCommand ? overrideCommand.Override : commandName; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -1,31 +0,0 @@ | |||||||
| import { injectable } from "tsyringe"; |  | ||||||
| import { IActivityState } from "../models/activityState"; |  | ||||||
| import { IActivity } from "../models/config"; |  | ||||||
|  |  | ||||||
| @injectable() |  | ||||||
| export class StateDataProvider { |  | ||||||
|   private _states: { |  | ||||||
|     [controlUnitName: string]: IActivityState | undefined; |  | ||||||
|   } = {}; |  | ||||||
|  |  | ||||||
|   public updateState(activity: IActivity, controlUnitName: string): void { |  | ||||||
|     this._states[controlUnitName] = { currentActivity: activity }; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   public removeState(controlUnitName: string): void { |  | ||||||
|     this._states[controlUnitName] = undefined; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   public getState(controlUnitName: string) { |  | ||||||
|     if (!this._states[controlUnitName]) { |  | ||||||
|       return undefined; |  | ||||||
|     } |  | ||||||
|     return this._states[controlUnitName]!.currentActivity; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   public get states(): { |  | ||||||
|     [controlUnitName: string]: IActivityState | undefined; |  | ||||||
|   } { |  | ||||||
|     return this._states; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
							
								
								
									
										184
									
								
								src/index.ts
									
									
									
									
									
								
							
							
						
						
									
										184
									
								
								src/index.ts
									
									
									
									
									
								
							| @@ -1,11 +1,183 @@ | |||||||
| import { API } from "homebridge"; | import { ControlUnit } from "./Accessories/ControlUnit"; | ||||||
|  | import { Activity } from "./Models/Activity"; | ||||||
|  | import { DeviceSetupItem } from "./Models/DeviceSetupItem"; | ||||||
|  | import { Input, Output, Matrix } from "./Models/Matrix"; | ||||||
|  | import HubDataProvider from './DataProviders/HubDataProvider'; | ||||||
|  |  | ||||||
| import { PLATFORM_NAME } from "./settings"; | let Accessory: any; | ||||||
| import { Platform } from "./platform"; | let Homebridge: any; | ||||||
|  |  | ||||||
|  | export type hub = "Harmony" | "Neeo"; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * This method registers the platform with Homebridge |  * Main entry. | ||||||
|  |  * @param homebridge  | ||||||
|  */ |  */ | ||||||
| export = (api: API) => { | export default function (homebridge: any) { | ||||||
|   api.registerPlatform(PLATFORM_NAME, Platform); |     Homebridge = homebridge; | ||||||
|  |     Accessory = homebridge.platformAccessory; | ||||||
|  |     homebridge.registerPlatform( | ||||||
|  |         'homebridge-harmony-watson', | ||||||
|  |         'HarmonyHubMatrix', | ||||||
|  |         HarmonyMatrixPlatform, | ||||||
|  |         true | ||||||
|  |     ); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | class HarmonyMatrixPlatform { | ||||||
|  |     log: any = {}; | ||||||
|  |     config: any = {}; | ||||||
|  |     api: any; | ||||||
|  |     externalAccessories: Array<any> = []; | ||||||
|  |     dataProvider: HubDataProvider | null; | ||||||
|  |  | ||||||
|  |     constructor(log: any, config: any, api: any) { | ||||||
|  |         this.log = log; | ||||||
|  |         this.config = config; | ||||||
|  |         this.api = api; | ||||||
|  |         this.log('INFO - Registering Harmony Matrix Platform'); | ||||||
|  |         this.api.on('didFinishLaunching', this.didFinishLaunching.bind(this)); | ||||||
|  |  | ||||||
|  |         this.dataProvider = null; | ||||||
|  |         this.log("This is new"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Handler for didFinishLaunching | ||||||
|  |      * Happens after constructor | ||||||
|  |      */ | ||||||
|  |     didFinishLaunching() { | ||||||
|  |         this.log(`Publishing external accessories`); | ||||||
|  |  | ||||||
|  |         //This is required in order to have multiple tv remotes on one platform | ||||||
|  |         this.externalAccessories.forEach((accessory: ControlUnit) => { | ||||||
|  |             this.api.publishExternalAccessories("HarmonyMatrixPlatform", [accessory.platformAccessory]); | ||||||
|  |         }) | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Called by homebridge to gather accessories. | ||||||
|  |      * @param callback  | ||||||
|  |      */ | ||||||
|  |     accessories(callback: (accessories: Array<ControlUnit>) => void) { | ||||||
|  |         //Parse ip | ||||||
|  |         let harmonyIp: string = this.config["harmonyHubIp"]; | ||||||
|  |         let neeoIp: string = this.config["neeoHubIp"]; | ||||||
|  |  | ||||||
|  |         //Parse matrix | ||||||
|  |         let configInputs: any = this.config["Matrix"]["Inputs"]; | ||||||
|  |         let configOutputs: any = this.config["Matrix"]["Outputs"]; | ||||||
|  |         let matrixName: string = this.config["Matrix"]["DeviceName"]; | ||||||
|  |         let hub: hub = this.config["Matrix"]["Hub"]; | ||||||
|  |  | ||||||
|  |         let inputs: Array<Input> = []; | ||||||
|  |         let outputs: Array<Output> = []; | ||||||
|  |  | ||||||
|  |         configInputs.forEach((configInput: any) => { | ||||||
|  |             let inputDevice: string = configInput["InputDevice"]; | ||||||
|  |             let inputNumber: string = configInput["InputNumber"]; | ||||||
|  |  | ||||||
|  |             this.log(`INFO - Added input to matrix '${inputDevice}'`); | ||||||
|  |  | ||||||
|  |             inputs.push({ | ||||||
|  |                 inputDevice: inputDevice, | ||||||
|  |                 inputNumber: inputNumber | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |         configOutputs.forEach((configOutput: any) => { | ||||||
|  |             let outputDevice: string = configOutput["OutputDevice"]; | ||||||
|  |             let outputLetter: string = configOutput["OutputLetter"]; | ||||||
|  |  | ||||||
|  |             this.log(`INFO - Added output to matrix '${outputDevice}'`); | ||||||
|  |  | ||||||
|  |             outputs.push({ | ||||||
|  |                 outputDevice: outputDevice, | ||||||
|  |                 outputLetter: outputLetter | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         let matrix = new Matrix({ | ||||||
|  |             inputs: inputs, | ||||||
|  |             outputs: outputs, | ||||||
|  |             deviceName: matrixName, | ||||||
|  |             hubType: hub | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         //construct data provider | ||||||
|  |         this.dataProvider = new HubDataProvider({ | ||||||
|  |             harmonyHubAddress: harmonyIp, | ||||||
|  |             neeoHubAddress: neeoIp, | ||||||
|  |             matrix: matrix, | ||||||
|  |             log: this.log | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         //Parse control units | ||||||
|  |         let configControlUnits: any = this.config["ControlUnits"]; | ||||||
|  |         let controlUnits: Array<ControlUnit> = []; | ||||||
|  |         configControlUnits.forEach((configControlUnit: any) => { | ||||||
|  |             //Parse activities list | ||||||
|  |             let configActivities: any = configControlUnit["Activities"]; | ||||||
|  |             let activities: Array<Activity> = []; | ||||||
|  |             configActivities.forEach((configActivity: any) => { | ||||||
|  |                 //parse devices | ||||||
|  |                 let configDevices: any = configActivity["DeviceSetupList"]; | ||||||
|  |                 let devices: Array<DeviceSetupItem> = []; | ||||||
|  |                 configDevices.forEach((configDevice: any) => { | ||||||
|  |                     //Add device | ||||||
|  |                     devices.push(new DeviceSetupItem({ | ||||||
|  |                         deviceName: configDevice["DeviceName"], | ||||||
|  |                         input: configDevice["Input"], | ||||||
|  |                         hub: configDevice["Hub"] | ||||||
|  |                     })); | ||||||
|  |                     this.log(`INFO - Added device '${configDevice["DeviceName"]}' for activity '${configActivity["DisplayName"]}'`); | ||||||
|  |                 }); | ||||||
|  |  | ||||||
|  |                 //Add activity | ||||||
|  |                 activities.push(new Activity({ | ||||||
|  |                     volumeDeviceId: configActivity["VolumeDevice"], | ||||||
|  |                     controlDeviceId: configActivity["ControlDevice"], | ||||||
|  |                     outputDeviceId: configActivity["OutputDevice"], | ||||||
|  |                     displayName: configActivity["DisplayName"], | ||||||
|  |                     useMatrix: configActivity["UseMatrix"], | ||||||
|  |                     deviceList: devices | ||||||
|  |                 })); | ||||||
|  |                 this.log(`INFO - Added activity '${configActivity["DisplayName"]}'`); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             let controlUnit: ControlUnit = new ControlUnit({ | ||||||
|  |                 dataProvider: this.dataProvider!, | ||||||
|  |                 displayName: configControlUnit["DisplayName"], | ||||||
|  |                 api: this.api, | ||||||
|  |                 log: this.log, | ||||||
|  |                 activities: activities, | ||||||
|  |                 homebridge: Homebridge, | ||||||
|  |                 isExternal: false | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             let controlUnitExternal: ControlUnit = new ControlUnit({ | ||||||
|  |                 dataProvider: this.dataProvider!, | ||||||
|  |                 displayName: `${configControlUnit["DisplayName"]}`, | ||||||
|  |                 api: this.api, | ||||||
|  |                 log: this.log, | ||||||
|  |                 activities: activities, | ||||||
|  |                 homebridge: Homebridge, | ||||||
|  |                 isExternal: true | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             //@ts-ignore | ||||||
|  |             let accessory = controlUnit as homebridge.platformAccessory; | ||||||
|  |             //@ts-ignore | ||||||
|  |             let externalAccessory = controlUnitExternal as homebridge.platformAccessory; | ||||||
|  |  | ||||||
|  |             //Add control unit | ||||||
|  |             controlUnits.push(accessory); | ||||||
|  |             //Add to list of remotes | ||||||
|  |             this.externalAccessories.push(externalAccessory); | ||||||
|  |  | ||||||
|  |             this.log(`INFO - Added ControlUnit`); | ||||||
|  |         }); | ||||||
|  |         callback(controlUnits); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| import { IActivity } from "./config"; |  | ||||||
|  |  | ||||||
| export interface IActivityState { |  | ||||||
|   currentActivity: IActivity; |  | ||||||
| } |  | ||||||
| @@ -1,10 +0,0 @@ | |||||||
| import { IDeviceSetupItem } from "./deviceSetupItem"; |  | ||||||
|  |  | ||||||
| export interface IActivity { |  | ||||||
|   OutputDevice: string; |  | ||||||
|   VolumeDevice: string; |  | ||||||
|   ControlDevice: string; |  | ||||||
|   DisplayName: string; |  | ||||||
|   DeviceSetupList: Array<IDeviceSetupItem>; |  | ||||||
|   UseMatrix: boolean; |  | ||||||
| } |  | ||||||
| @@ -1,22 +0,0 @@ | |||||||
| import { IMatrix } from "./matrix"; |  | ||||||
| import { IActivity } from "./activity"; |  | ||||||
| import { IDeviceButton } from "./deviceButton"; |  | ||||||
| import { IDeviceConfig } from "./deviceConfig"; |  | ||||||
| import { IHub } from "./hub"; |  | ||||||
| import { ISequence } from "./sequence"; |  | ||||||
|  |  | ||||||
| export interface IControlUnit { |  | ||||||
|   DisplayName: string; |  | ||||||
|   Activities: Array<IActivity>; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export interface IConfig { |  | ||||||
|   hubIp: string; |  | ||||||
|   EmitDevicesOnStartup: boolean; |  | ||||||
|   Matrix: IMatrix; |  | ||||||
|   ControlUnits: Array<IControlUnit>; |  | ||||||
|   DeviceButtons: Array<IDeviceButton>; |  | ||||||
|   Sequences: Array<ISequence>; |  | ||||||
|   Devices: Array<IDeviceConfig>; |  | ||||||
|   Hubs: Array<IHub>; |  | ||||||
| } |  | ||||||
| @@ -1,7 +0,0 @@ | |||||||
| export interface IDeviceButton { |  | ||||||
|     DeviceName: string; |  | ||||||
|     ButtonName: string; |  | ||||||
|     DisplayName: string; |  | ||||||
|     NumberOfKeyPresses: number; |  | ||||||
|     IsStateful: boolean; |  | ||||||
| } |  | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| export interface IDeviceConfig { |  | ||||||
|   Name: string; |  | ||||||
|   Hub: string; |  | ||||||
|   Overrides: Array<{ Command: string; Override: string }>; |  | ||||||
| } |  | ||||||
| @@ -1,5 +0,0 @@ | |||||||
|  |  | ||||||
| export interface IDeviceSetupItem { |  | ||||||
|     DeviceName: string; |  | ||||||
|     Input: string; |  | ||||||
| } |  | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| export interface IHub { |  | ||||||
|     Name: string; |  | ||||||
|     Ip: string; |  | ||||||
|     Harmony: any; |  | ||||||
| } |  | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| export * from "./activity"; |  | ||||||
| export * from "./config"; |  | ||||||
| export * from "./deviceButton"; |  | ||||||
| export * from "./deviceSetupItem"; |  | ||||||
| export * from "./matrix"; |  | ||||||
| @@ -1,15 +0,0 @@ | |||||||
| export interface IInput { |  | ||||||
|     InputNumber: string, |  | ||||||
|     InputDevice: string, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export interface IOutput { |  | ||||||
|     OutputLetter: string, |  | ||||||
|     OutputDevice: string, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export interface IMatrix { |  | ||||||
|     Inputs: Array<IInput>; |  | ||||||
|     Outputs: Array<IOutput>; |  | ||||||
|     DeviceName: string; |  | ||||||
| } |  | ||||||
| @@ -1,10 +0,0 @@ | |||||||
| export interface ISequence { |  | ||||||
|   DisplayName: string; |  | ||||||
|   Steps: Array<IStep>; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export interface IStep { |  | ||||||
|   DeviceName?: string; |  | ||||||
|   DeviceCommand?: string; |  | ||||||
|   Delay: number; |  | ||||||
| } |  | ||||||
| @@ -1,19 +0,0 @@ | |||||||
| export interface ICommand { |  | ||||||
|     command?: string, |  | ||||||
|     deviceId?: string, |  | ||||||
|     type?: string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export interface IDevice { |  | ||||||
|     id: string; |  | ||||||
|     name: string; |  | ||||||
|     harmony: any; |  | ||||||
|     log: any |  | ||||||
|     supportsCommand(commandName: string): boolean, |  | ||||||
|     getCommand(commandName: string): ICommand, |  | ||||||
|     powerOn(): Promise<void>; |  | ||||||
|     powerOff(): Promise<void>; |  | ||||||
|     sendCommand(commandName: string): Promise<void>; |  | ||||||
|     commands: { [name: string]: ICommand }; |  | ||||||
|     on: boolean; |  | ||||||
| } |  | ||||||
| @@ -1,47 +0,0 @@ | |||||||
| import { ICommand } from "./device"; |  | ||||||
| import { sleep } from "../util/sleep"; |  | ||||||
|  |  | ||||||
| export interface IHarmonyDeviceProps { |  | ||||||
|   id: string; |  | ||||||
|   name: string; |  | ||||||
|   harmony: any; |  | ||||||
|   log: any; |  | ||||||
|   commands: { [name: string]: ICommand }; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export class HarmonyDevice { |  | ||||||
|   private _commands: { [name: string]: ICommand } = {}; |  | ||||||
|   private _on: boolean; |  | ||||||
|  |  | ||||||
|   constructor(props: IHarmonyDeviceProps) { |  | ||||||
|     this.id = props.id; |  | ||||||
|     this.name = props.name; |  | ||||||
|     this._on = false; |  | ||||||
|     this._commands = props.commands; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   public id: string; |  | ||||||
|   public name: string; |  | ||||||
|  |  | ||||||
|   public get on(): boolean { |  | ||||||
|     return this._on; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   public set on(value: boolean) { |  | ||||||
|     this._on = value; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   public get commands(): { [name: string]: ICommand } { |  | ||||||
|     return this._commands; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   //Define device methods |  | ||||||
|   public supportsCommand(commandName: string): boolean { |  | ||||||
|     let command = this._commands[commandName]; |  | ||||||
|     return command ? true : false; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   public getCommand(commandName: string): ICommand { |  | ||||||
|     return this._commands[commandName]; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -1,94 +0,0 @@ | |||||||
| import { HarmonyDevice } from "./harmonyDevice"; |  | ||||||
| const Harmony = require("harmony-websocket"); |  | ||||||
| import { ICommand } from "./device"; |  | ||||||
| import { EventEmitter } from "events"; |  | ||||||
| import { sleep } from "../util"; |  | ||||||
|  |  | ||||||
| export class HarmonyHub extends EventEmitter { |  | ||||||
|   private _devices: { [deviceName: string]: HarmonyDevice } = {}; |  | ||||||
|   private _ip: string; |  | ||||||
|   private _harmony: any; |  | ||||||
|   private _log: any; |  | ||||||
|   private _name: string; |  | ||||||
|  |  | ||||||
|   constructor(hubName: string, ipAddress: string, log: any) { |  | ||||||
|     super(); |  | ||||||
|     this._ip = ipAddress; |  | ||||||
|     this._log = log; |  | ||||||
|     this._name = hubName; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   public get devices(): { [deviceName: string]: HarmonyDevice } { |  | ||||||
|     return this._devices; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   public get hubName(): string { |  | ||||||
|     return this._name; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   public getDeviceByName = (deviceName: string): HarmonyDevice => { |  | ||||||
|     return this._devices[deviceName]; |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   public initialize = async () => { |  | ||||||
|     this._harmony = new Harmony(); |  | ||||||
|     await this._harmony.connect(this._ip); |  | ||||||
|     this._harmony.on("stateDigest", (data: any) => { |  | ||||||
|       console.log(data); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     this._harmony.on("automationState", (data: any) => { |  | ||||||
|       console.log(data); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     //Gather devices |  | ||||||
|     let devices: any = await this._harmony.getDevices(); |  | ||||||
|     try { |  | ||||||
|       await Promise.all( |  | ||||||
|         //Add each to dictionary |  | ||||||
|         devices.map(async (dev: any) => { |  | ||||||
|           //get commands |  | ||||||
|           let commands: { [name: string]: ICommand } = {}; |  | ||||||
|           let deviceCommands: any = await this._harmony.getDeviceCommands( |  | ||||||
|             dev.id |  | ||||||
|           ); |  | ||||||
|           deviceCommands.forEach((command: any) => { |  | ||||||
|             commands[command.label] = command.action; |  | ||||||
|           }); |  | ||||||
|           this._devices[dev.label] = new HarmonyDevice({ |  | ||||||
|             id: dev.id, |  | ||||||
|             name: dev.label, |  | ||||||
|             commands: commands, |  | ||||||
|             log: this._log, |  | ||||||
|             harmony: this._harmony, |  | ||||||
|           }); |  | ||||||
|         }) |  | ||||||
|       ); |  | ||||||
|       this.emit("Ready"); |  | ||||||
|     } catch (err) { |  | ||||||
|       this._log(`ERROR - error connecting to harmony: ${err}`); |  | ||||||
|     } |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   private connect = async (): Promise<void> => { |  | ||||||
|     await this._harmony.Connect(this._ip); |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   public async sendCommand(command: ICommand): Promise<void> { |  | ||||||
|     try { |  | ||||||
|       //Execute command |  | ||||||
|       //HACK to fix Harmon Kardon receiver not turning off |  | ||||||
|       if (command.command === "PowerOff") { |  | ||||||
|         for (let i = 0; i < 2; i++) { |  | ||||||
|           await this._harmony.sendCommand(JSON.stringify(command)); |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|       await this._harmony.sendCommand(JSON.stringify(command)); |  | ||||||
|  |  | ||||||
|       //Sleep |  | ||||||
|       await sleep(800); |  | ||||||
|     } catch (err) { |  | ||||||
|       this._log(`ERROR - error sending command to harmony: ${err}`); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -1 +0,0 @@ | |||||||
| export * from "./device"; |  | ||||||
							
								
								
									
										228
									
								
								src/platform.ts
									
									
									
									
									
								
							
							
						
						
									
										228
									
								
								src/platform.ts
									
									
									
									
									
								
							| @@ -1,228 +0,0 @@ | |||||||
| import "reflect-metadata"; |  | ||||||
| import { |  | ||||||
|   API, |  | ||||||
|   Characteristic, |  | ||||||
|   DynamicPlatformPlugin, |  | ||||||
|   Logger, |  | ||||||
|   PlatformAccessory, |  | ||||||
|   PlatformConfig, |  | ||||||
|   Service, |  | ||||||
| } from "homebridge"; |  | ||||||
| import { ControlUnit, DeviceButton } from "./accessories"; |  | ||||||
| import { Sequence } from "./accessories/sequence"; |  | ||||||
| import { IConfig, IControlUnit, IDeviceButton } from "./models/config"; |  | ||||||
| import { ISequence } from "./models/config/sequence"; |  | ||||||
| import { PLATFORM_NAME, PLUGIN_NAME } from "./settings"; |  | ||||||
| import { container } from "tsyringe"; |  | ||||||
| import { HarmonyDataProvider } from "./dataProviders/harmonyDataProvider"; |  | ||||||
| import { StateDataProvider } from "./dataProviders/stateDataProvider"; |  | ||||||
| import { CommandService } from "./services/commandService"; |  | ||||||
| import { ActivityService } from "./services/activityService"; |  | ||||||
| import { VolumeService } from "./services/volumeService"; |  | ||||||
|  |  | ||||||
| export class Platform implements DynamicPlatformPlugin { |  | ||||||
|   constructor( |  | ||||||
|     public readonly log: Logger, |  | ||||||
|     config: PlatformConfig, |  | ||||||
|     public readonly api: API |  | ||||||
|   ) { |  | ||||||
|     this.log.debug("Finished initializing platform:", config.name); |  | ||||||
|     this.config = config as unknown as IConfig; |  | ||||||
|     this.register(); |  | ||||||
|  |  | ||||||
|     let didFinishLaunching = false; |  | ||||||
|     this.api.on("didFinishLaunching", async () => { |  | ||||||
|       log.debug("Executed didFinishLaunching callback"); |  | ||||||
|       this.discoverControlUnitAccessories(); |  | ||||||
|       this.discoverDeviceButtonAccessories(); |  | ||||||
|       this.discoverSequenceAccessories(); |  | ||||||
|       this.pruneAccessories(); |  | ||||||
|       didFinishLaunching = true; |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   public readonly Service: typeof Service = this.api.hap.Service; |  | ||||||
|   public readonly Characteristic: typeof Characteristic = |  | ||||||
|     this.api.hap.Characteristic; |  | ||||||
|  |  | ||||||
|   // this is used to track restored cached accessories |  | ||||||
|   public readonly accessories: PlatformAccessory[] = []; |  | ||||||
|   public config: IConfig; |  | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * Discover new control unit accessories |  | ||||||
|    * @param dataProvider |  | ||||||
|    */ |  | ||||||
|   private discoverControlUnitAccessories(): void { |  | ||||||
|     this.config.ControlUnits.forEach((unit: IControlUnit) => { |  | ||||||
|       const uuid = this.api.hap.uuid.generate(unit.DisplayName); |  | ||||||
|       const existingAccessory = this.accessories.find((e) => e.UUID === uuid); |  | ||||||
|  |  | ||||||
|       if (existingAccessory) { |  | ||||||
|         this.log.info( |  | ||||||
|           "Restoring existing accessory from cache: " + |  | ||||||
|             existingAccessory.displayName |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         new ControlUnit( |  | ||||||
|           this, |  | ||||||
|           existingAccessory, |  | ||||||
|           container.resolve(ActivityService), |  | ||||||
|           container.resolve(CommandService), |  | ||||||
|           container.resolve(VolumeService), |  | ||||||
|           unit.Activities |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         this.api.publishExternalAccessories(PLUGIN_NAME, [existingAccessory]); |  | ||||||
|         console.log("Publishing external accessory: " + uuid); |  | ||||||
|       } else { |  | ||||||
|         this.log.info("Adding new accessory: " + unit.DisplayName); |  | ||||||
|         const accessory = new this.api.platformAccessory( |  | ||||||
|           unit.DisplayName, |  | ||||||
|           uuid |  | ||||||
|         ); |  | ||||||
|         accessory.context["DeviceName"] = unit.DisplayName; |  | ||||||
|         accessory.context["Type"] = typeof ControlUnit.name; |  | ||||||
|  |  | ||||||
|         new ControlUnit( |  | ||||||
|           this, |  | ||||||
|           accessory, |  | ||||||
|           container.resolve(ActivityService), |  | ||||||
|           container.resolve(CommandService), |  | ||||||
|           container.resolve(VolumeService), |  | ||||||
|           unit.Activities |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         this.api.publishExternalAccessories(PLUGIN_NAME, [accessory]); |  | ||||||
|         console.log("Publishing external accessory: " + uuid); |  | ||||||
|       } |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * Discover new device button accessories |  | ||||||
|    * @param dataProvider |  | ||||||
|    */ |  | ||||||
|   private discoverDeviceButtonAccessories(): void { |  | ||||||
|     this.config.DeviceButtons.forEach((button: IDeviceButton) => { |  | ||||||
|       const uuid = this.api.hap.uuid.generate(button.DisplayName); |  | ||||||
|       const existingAccessory = this.accessories.find((e) => e.UUID === uuid); |  | ||||||
|       if (existingAccessory) { |  | ||||||
|         this.log.info( |  | ||||||
|           "Restoring existing accessory from cache: " + |  | ||||||
|             existingAccessory.displayName |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         new DeviceButton( |  | ||||||
|           this, |  | ||||||
|           existingAccessory, |  | ||||||
|           container.resolve(HarmonyDataProvider), |  | ||||||
|           button |  | ||||||
|         ); |  | ||||||
|         this.api.updatePlatformAccessories([existingAccessory]); |  | ||||||
|       } else { |  | ||||||
|         this.log.info("Adding new accessory: " + button.DisplayName); |  | ||||||
|         const accessory = new this.api.platformAccessory( |  | ||||||
|           button.DisplayName, |  | ||||||
|           uuid |  | ||||||
|         ); |  | ||||||
|         accessory.context["DeviceName"] = button.DisplayName; |  | ||||||
|         accessory.context["Type"] = typeof DeviceButton; |  | ||||||
|  |  | ||||||
|         new DeviceButton( |  | ||||||
|           this, |  | ||||||
|           accessory, |  | ||||||
|           container.resolve(HarmonyDataProvider), |  | ||||||
|           button |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [ |  | ||||||
|           accessory, |  | ||||||
|         ]); |  | ||||||
|         this.accessories.push(accessory); |  | ||||||
|       } |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * Discover new sequence accessories |  | ||||||
|    * @param dataProvider |  | ||||||
|    */ |  | ||||||
|   private discoverSequenceAccessories(): void { |  | ||||||
|     this.config.Sequences.forEach((sequence: ISequence) => { |  | ||||||
|       const uuid = this.api.hap.uuid.generate(sequence.DisplayName); |  | ||||||
|       const existingAccessory = this.accessories.find((e) => e.UUID === uuid); |  | ||||||
|       if (existingAccessory) { |  | ||||||
|         this.log.info( |  | ||||||
|           "Restoring existing accessory from cache: " + |  | ||||||
|             existingAccessory.displayName |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         new Sequence( |  | ||||||
|           this, |  | ||||||
|           existingAccessory, |  | ||||||
|           container.resolve(HarmonyDataProvider), |  | ||||||
|           sequence |  | ||||||
|         ); |  | ||||||
|         this.api.updatePlatformAccessories([existingAccessory]); |  | ||||||
|       } else { |  | ||||||
|         this.log.info("Adding new accessory: " + sequence.DisplayName); |  | ||||||
|         const accessory = new this.api.platformAccessory( |  | ||||||
|           sequence.DisplayName, |  | ||||||
|           uuid |  | ||||||
|         ); |  | ||||||
|         accessory.context["DeviceName"] = sequence.DisplayName; |  | ||||||
|         accessory.context["Type"] = typeof "Sequence"; |  | ||||||
|  |  | ||||||
|         new Sequence( |  | ||||||
|           this, |  | ||||||
|           accessory, |  | ||||||
|           container.resolve(HarmonyDataProvider), |  | ||||||
|           sequence |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [ |  | ||||||
|           accessory, |  | ||||||
|         ]); |  | ||||||
|         this.accessories.push(accessory); |  | ||||||
|       } |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   private pruneAccessories(): void { |  | ||||||
|     // Remove devices not discovered |  | ||||||
|     const listOfConfiguredAccessories = [ |  | ||||||
|       ...this.config.DeviceButtons.map((e) => e.DisplayName), |  | ||||||
|       ...this.config.Sequences.map((e) => e.DisplayName), |  | ||||||
|       ...this.config.ControlUnits.map((e) => e.DisplayName), |  | ||||||
|     ]; |  | ||||||
|     for (const accessory of this.accessories) { |  | ||||||
|       if ( |  | ||||||
|         listOfConfiguredAccessories.filter( |  | ||||||
|           (displayName) => displayName === accessory.displayName |  | ||||||
|         ).length === 0 |  | ||||||
|       ) { |  | ||||||
|         this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [ |  | ||||||
|           accessory, |  | ||||||
|         ]); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   configureAccessory(accessory: PlatformAccessory<Record<string, any>>): void { |  | ||||||
|     this.log.info("Loading accessory from cache:", accessory.displayName); |  | ||||||
|  |  | ||||||
|     // add the restored accessory to the accessories cache so we can track if it has already been registered |  | ||||||
|     this.accessories.push(accessory); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   private register(): void { |  | ||||||
|     container.register<Platform>(Platform, { useValue: this }); |  | ||||||
|     container.register<IConfig>("IConfig", { useValue: this.config }); |  | ||||||
|     container.register<Logger>("log", { useValue: this.log }); |  | ||||||
|     container.registerSingleton<HarmonyDataProvider>(HarmonyDataProvider); |  | ||||||
|     container.registerSingleton<ActivityService>(ActivityService); |  | ||||||
|     container.registerSingleton<StateDataProvider>(StateDataProvider); |  | ||||||
|     container.registerSingleton<CommandService>(CommandService); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -1,226 +0,0 @@ | |||||||
| import { Logging } from "homebridge"; |  | ||||||
| import { inject, injectable } from "tsyringe"; |  | ||||||
| import { HarmonyDataProvider } from "../dataProviders/harmonyDataProvider"; |  | ||||||
| import { StateDataProvider } from "../dataProviders/stateDataProvider"; |  | ||||||
| import { IActivityState } from "../models/activityState"; |  | ||||||
| import { |  | ||||||
|   IActivity, |  | ||||||
|   IConfig, |  | ||||||
|   IDeviceSetupItem, |  | ||||||
|   IInput, |  | ||||||
|   IOutput, |  | ||||||
| } from "../models/config"; |  | ||||||
| import { HarmonyDevice } from "../models/harmonyDevice"; |  | ||||||
|  |  | ||||||
| @injectable() |  | ||||||
| export class ActivityService { |  | ||||||
|   constructor( |  | ||||||
|     @inject(HarmonyDataProvider) |  | ||||||
|     private _harmonyDataProvider: HarmonyDataProvider, |  | ||||||
|     @inject(StateDataProvider) private _stateDataProvider: StateDataProvider, |  | ||||||
|     @inject("IConfig") private _config: IConfig, |  | ||||||
|     @inject("log") private _log: Logging |  | ||||||
|   ) {} |  | ||||||
|  |  | ||||||
|   public startActivity = async ( |  | ||||||
|     controlUnitName: string, |  | ||||||
|     activity: IActivity |  | ||||||
|   ) => { |  | ||||||
|     this._log.info( |  | ||||||
|       `Starting activity ${activity.DisplayName} for controlUnit: ${controlUnitName}` |  | ||||||
|     ); |  | ||||||
|     let devicesToTurnOn: Array<HarmonyDevice> = this.getDevicesToTurnOn( |  | ||||||
|       activity, |  | ||||||
|       controlUnitName |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     await this._harmonyDataProvider.powerOnDevices(devicesToTurnOn); |  | ||||||
|  |  | ||||||
|     await this.assignDeviceInput(activity); |  | ||||||
|  |  | ||||||
|     await this.routeInputsToOutputs(activity); |  | ||||||
|  |  | ||||||
|     let lastActivity = this.getCurrentActivity(controlUnitName); |  | ||||||
|  |  | ||||||
|     let devicesToTurnOff: Array<HarmonyDevice> = this.getDevicesToTurnOff( |  | ||||||
|       controlUnitName, |  | ||||||
|       lastActivity, |  | ||||||
|       activity |  | ||||||
|     ); |  | ||||||
|     await this._harmonyDataProvider.powerOffDevices(devicesToTurnOff); |  | ||||||
|  |  | ||||||
|     this._stateDataProvider.updateState(activity, controlUnitName); |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   public stopCurrentActivity = async ( |  | ||||||
|     controlUnitName: string |  | ||||||
|   ): Promise<void> => { |  | ||||||
|     if (!this.getCurrentActivity(controlUnitName)) { |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     let lastActivity: IActivity | undefined = |  | ||||||
|       this.getCurrentActivity(controlUnitName); |  | ||||||
|  |  | ||||||
|     let devicesToTurnOff: Array<HarmonyDevice> = this.getDevicesToTurnOff( |  | ||||||
|       controlUnitName, |  | ||||||
|       lastActivity |  | ||||||
|     ); |  | ||||||
|     await this._harmonyDataProvider.powerOffDevices(devicesToTurnOff); |  | ||||||
|  |  | ||||||
|     this._stateDataProvider.removeState(controlUnitName); |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   public getCurrentActivity(controlUnitName: string): IActivity | undefined { |  | ||||||
|     return this._stateDataProvider.getState(controlUnitName); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * Return if a control unit is active |  | ||||||
|    * @param controlUnitName |  | ||||||
|    */ |  | ||||||
|   public getIsActive(controlUnitName: string): IActivity | undefined { |  | ||||||
|     return this._stateDataProvider.getState(controlUnitName); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * Helper function to make sure no control unit depends on device list. |  | ||||||
|    * @param devicesToTurnOn The list of devices to modify. |  | ||||||
|    * @param controlUnitName The name of the control unit in question. |  | ||||||
|    */ |  | ||||||
|   private sanitizeDeviceList( |  | ||||||
|     devicesToTurnOn: Array<HarmonyDevice>, |  | ||||||
|     controlUnitName: string |  | ||||||
|   ): Array<HarmonyDevice> { |  | ||||||
|     for (let controlUnitKey in this._stateDataProvider.states) { |  | ||||||
|       //Skip self |  | ||||||
|       if (controlUnitKey === controlUnitName) { |  | ||||||
|         continue; |  | ||||||
|       } |  | ||||||
|       let currentOtherState: IActivityState = |  | ||||||
|         this._stateDataProvider.states[controlUnitKey]!; |  | ||||||
|  |  | ||||||
|       if (!currentOtherState) { |  | ||||||
|         return devicesToTurnOn; |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       currentOtherState.currentActivity.DeviceSetupList.forEach( |  | ||||||
|         (value: IDeviceSetupItem) => { |  | ||||||
|           //there are devices to remove |  | ||||||
|           if (devicesToTurnOn.some((e) => e && e.name === value.DeviceName)) { |  | ||||||
|             let deviceToRemove: HarmonyDevice = devicesToTurnOn.filter( |  | ||||||
|               (i) => i.name === value.DeviceName |  | ||||||
|             )[0]; |  | ||||||
|             delete devicesToTurnOn[devicesToTurnOn.indexOf(deviceToRemove)]; |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return devicesToTurnOn; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   private getDevicesToTurnOn( |  | ||||||
|     activity: IActivity, |  | ||||||
|     controlUnitName: string |  | ||||||
|   ): Array<HarmonyDevice> { |  | ||||||
|     let potentialDevices = this.buildPotentialDeviceList(activity); |  | ||||||
|     return this.sanitizeDeviceList(potentialDevices, controlUnitName); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   private getDevicesToTurnOff( |  | ||||||
|     controlUnitName: string, |  | ||||||
|     lastActivity?: IActivity, |  | ||||||
|     nextActivity?: IActivity |  | ||||||
|   ) { |  | ||||||
|     let potentialDevices = lastActivity |  | ||||||
|       ? this.buildPotentialDeviceList(lastActivity) |  | ||||||
|       : []; |  | ||||||
|  |  | ||||||
|     //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 |  | ||||||
|     if (nextActivity) { |  | ||||||
|       potentialDevices.forEach((device: HarmonyDevice, index: number) => { |  | ||||||
|         if ( |  | ||||||
|           device && |  | ||||||
|           device.name && |  | ||||||
|           nextActivity.DeviceSetupList.some((e) => { |  | ||||||
|             return e && e.DeviceName === device.name; |  | ||||||
|           }) |  | ||||||
|         ) { |  | ||||||
|           delete potentialDevices[index]; |  | ||||||
|         } |  | ||||||
|       }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return this.sanitizeDeviceList(potentialDevices, controlUnitName); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   private buildPotentialDeviceList(activity: IActivity): HarmonyDevice[] { |  | ||||||
|     return activity.DeviceSetupList.map( |  | ||||||
|       (value: IDeviceSetupItem): HarmonyDevice => { |  | ||||||
|         return this._harmonyDataProvider.getDeviceByName(value.DeviceName); |  | ||||||
|       } |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   private async assignDeviceInput(activity: IActivity): Promise<void> { |  | ||||||
|     await Promise.all( |  | ||||||
|       activity.DeviceSetupList.map(async (value: IDeviceSetupItem) => { |  | ||||||
|         let device: HarmonyDevice = this._harmonyDataProvider.getDeviceByName( |  | ||||||
|           value.DeviceName |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         if (device && device.supportsCommand(`Input${value.Input}`)) { |  | ||||||
|           await this._harmonyDataProvider.sendCommand( |  | ||||||
|             `Input${value.Input}`, |  | ||||||
|             device |  | ||||||
|           ); |  | ||||||
|         } |  | ||||||
|       }) |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   private async routeInputsToOutputs(activity: IActivity) { |  | ||||||
|     if (activity.UseMatrix) { |  | ||||||
|       //get input and output |  | ||||||
|       let input: IInput = this._config.Matrix.Inputs.filter( |  | ||||||
|         (e) => e.InputDevice === activity.ControlDevice |  | ||||||
|       )[0]; |  | ||||||
|       let output: IOutput = this._config.Matrix.Outputs.filter( |  | ||||||
|         (e) => e.OutputDevice === activity.OutputDevice |  | ||||||
|       )[0]; |  | ||||||
|  |  | ||||||
|       let inputCommandName: string = `In ${input.InputNumber}`; |  | ||||||
|       let outputCommandName: string = `Out ${output.OutputLetter}`; |  | ||||||
|  |  | ||||||
|       let matrixDevice: HarmonyDevice = |  | ||||||
|         this._harmonyDataProvider.getDeviceByName( |  | ||||||
|           this._config.Matrix.DeviceName |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|       //Route hdmi |  | ||||||
|       if ( |  | ||||||
|         matrixDevice.supportsCommand(inputCommandName) && |  | ||||||
|         matrixDevice.supportsCommand(outputCommandName) |  | ||||||
|       ) { |  | ||||||
|         await this._harmonyDataProvider.sendCommand( |  | ||||||
|           outputCommandName, |  | ||||||
|           matrixDevice |  | ||||||
|         ); |  | ||||||
|         await this._harmonyDataProvider.sendCommand( |  | ||||||
|           inputCommandName, |  | ||||||
|           matrixDevice |  | ||||||
|         ); |  | ||||||
|         await this._harmonyDataProvider.sendCommand( |  | ||||||
|           outputCommandName, |  | ||||||
|           matrixDevice |  | ||||||
|         ); |  | ||||||
|         await this._harmonyDataProvider.sendCommand( |  | ||||||
|           inputCommandName, |  | ||||||
|           matrixDevice |  | ||||||
|         ); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -1,71 +0,0 @@ | |||||||
| import { inject, injectable } from "tsyringe"; |  | ||||||
| import { RemoteKey } from "../accessories/controlUnit"; |  | ||||||
| import { HarmonyDataProvider } from "../dataProviders/harmonyDataProvider"; |  | ||||||
| import { StateDataProvider } from "../dataProviders/stateDataProvider"; |  | ||||||
| import { HarmonyDevice } from "../models/harmonyDevice"; |  | ||||||
|  |  | ||||||
| @injectable() |  | ||||||
| export class CommandService { |  | ||||||
|   constructor( |  | ||||||
|     @inject(StateDataProvider) private _stateDataProvider: StateDataProvider, |  | ||||||
|     @inject(HarmonyDataProvider) |  | ||||||
|     private _harmonyDataProvider: HarmonyDataProvider |  | ||||||
|   ) {} |  | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * 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) => { |  | ||||||
|     let currentActivity = this._stateDataProvider.getState(controlUnitName); |  | ||||||
|     if (currentActivity) { |  | ||||||
|       let commandName: string = ""; |  | ||||||
|  |  | ||||||
|       let device: HarmonyDevice = this._harmonyDataProvider.getDeviceByName( |  | ||||||
|         currentActivity.ControlDevice |  | ||||||
|       ); |  | ||||||
|       switch (key) { |  | ||||||
|         case RemoteKey.ARROW_UP: { |  | ||||||
|           commandName = "Direction Up"; |  | ||||||
|           break; |  | ||||||
|         } |  | ||||||
|         case RemoteKey.ARROW_DOWN: { |  | ||||||
|           commandName = "Direction Down"; |  | ||||||
|           break; |  | ||||||
|         } |  | ||||||
|         case RemoteKey.ARROW_LEFT: { |  | ||||||
|           commandName = "Direction Left"; |  | ||||||
|           break; |  | ||||||
|         } |  | ||||||
|         case RemoteKey.ARROW_RIGHT: { |  | ||||||
|           commandName = "Direction Right"; |  | ||||||
|           break; |  | ||||||
|         } |  | ||||||
|         case RemoteKey.SELECT: { |  | ||||||
|           commandName = "Select"; |  | ||||||
|           break; |  | ||||||
|         } |  | ||||||
|         case RemoteKey.PLAY_PAUSE: { |  | ||||||
|           commandName = "Pause"; |  | ||||||
|           break; |  | ||||||
|         } |  | ||||||
|         case RemoteKey.INFORMATION: { |  | ||||||
|           commandName = "Menu"; |  | ||||||
|           break; |  | ||||||
|         } |  | ||||||
|         case RemoteKey.BACK: { |  | ||||||
|           commandName = "Back"; |  | ||||||
|           break; |  | ||||||
|         } |  | ||||||
|         case RemoteKey.EXIT: { |  | ||||||
|           commandName = "Back"; |  | ||||||
|           break; |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       await this._harmonyDataProvider.sendCommand(commandName, device); |  | ||||||
|     } |  | ||||||
|   }; |  | ||||||
| } |  | ||||||
| @@ -1,45 +0,0 @@ | |||||||
| import { inject, injectable } from "tsyringe"; |  | ||||||
| import { HarmonyDataProvider } from "../dataProviders/harmonyDataProvider"; |  | ||||||
| import { StateDataProvider } from "../dataProviders/stateDataProvider"; |  | ||||||
| import { HarmonyDevice } from "../models/harmonyDevice"; |  | ||||||
|  |  | ||||||
| @injectable() |  | ||||||
| export class VolumeService { |  | ||||||
|   constructor( |  | ||||||
|     @inject(StateDataProvider) private _stateDataProvider: StateDataProvider, |  | ||||||
|     @inject(HarmonyDataProvider) |  | ||||||
|     private _harmonyDataProvider: HarmonyDataProvider |  | ||||||
|   ) {} |  | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * Turn the volume up for the current running activity. |  | ||||||
|    */ |  | ||||||
|   public volumeUp = async (controlUnitName: string) => { |  | ||||||
|     let volumeUpCommand: string = "Volume Up"; |  | ||||||
|     let currentActivity = this._stateDataProvider.getState(controlUnitName); |  | ||||||
|     if (currentActivity) { |  | ||||||
|       let volumeDevice: HarmonyDevice = |  | ||||||
|         this._harmonyDataProvider.getDeviceByName(currentActivity.VolumeDevice); |  | ||||||
|       await this._harmonyDataProvider.sendCommand( |  | ||||||
|         volumeUpCommand, |  | ||||||
|         volumeDevice |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   /** |  | ||||||
|    * Volume down for current running activity. |  | ||||||
|    */ |  | ||||||
|   public volumeDown = async (controlUnitName: string) => { |  | ||||||
|     let volumeDownCommand: string = "Volume Down"; |  | ||||||
|     let currentActivity = this._stateDataProvider.getState(controlUnitName); |  | ||||||
|     if (currentActivity) { |  | ||||||
|       let volumeDevice: HarmonyDevice = |  | ||||||
|         this._harmonyDataProvider.getDeviceByName(currentActivity.VolumeDevice); |  | ||||||
|       await this._harmonyDataProvider.sendCommand( |  | ||||||
|         volumeDownCommand, |  | ||||||
|         volumeDevice |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
|   }; |  | ||||||
| } |  | ||||||
| @@ -1,9 +0,0 @@ | |||||||
| /** |  | ||||||
|  * This is the name of the platform that users will use to register the plugin in the Homebridge config.json |  | ||||||
|  */ |  | ||||||
| export const PLATFORM_NAME = "HarmonyHubMatrix"; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * This must match the name of your plugin as defined the package.json |  | ||||||
|  */ |  | ||||||
| export const PLUGIN_NAME = "@watsonb8/homebridge-harmony-control"; |  | ||||||
| @@ -1,32 +0,0 @@ | |||||||
| /** |  | ||||||
|  * Helper function to convert callbacks into promises |  | ||||||
|  * @param func |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| export default function callbackify( |  | ||||||
|   func: (...args: any[]) => Promise<any> |  | ||||||
| ): Function { |  | ||||||
|   return (...args: any[]) => { |  | ||||||
|     const onlyArgs: any[] = []; |  | ||||||
|     let maybeCallback: Function | null = null; |  | ||||||
|  |  | ||||||
|     for (const arg of args) { |  | ||||||
|       if (typeof arg === "function") { |  | ||||||
|         maybeCallback = arg; |  | ||||||
|         break; |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       onlyArgs.push(arg); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (!maybeCallback) { |  | ||||||
|       throw new Error("Missing callback parameter!"); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     const callback = maybeCallback; |  | ||||||
|  |  | ||||||
|     func(...onlyArgs) |  | ||||||
|       .then((data: any) => callback(null, data)) |  | ||||||
|       .catch((err: any) => callback(err)); |  | ||||||
|   }; |  | ||||||
| } |  | ||||||
| @@ -1,2 +0,0 @@ | |||||||
| export * from "./callbackify"; |  | ||||||
| export * from "./sleep"; |  | ||||||
| @@ -1,15 +1,17 @@ | |||||||
| { | { | ||||||
|   "compilerOptions": { |     "compilerOptions": { | ||||||
|     "target": "es6", |         "target": "es6", | ||||||
|     "module": "commonjs", |         "module": "commonjs", | ||||||
|     "declaration": true, |         "declaration": true, | ||||||
|     "outDir": "./bin", |         "outDir": "./bin", | ||||||
|     "strict": true, |         "strict": true, | ||||||
|     "sourceMap": true, |         "sourceMap": true, | ||||||
|     "skipLibCheck": true, |     }, | ||||||
|     "experimentalDecorators": true, |     "include": [ | ||||||
|     "emitDecoratorMetadata": true |         "src" | ||||||
|   }, |     ], | ||||||
|   "include": ["src"], |     "exclude": [ | ||||||
|   "exclude": ["**/node_modules", "**/__tests__/*"] |         "node_modules", | ||||||
| } |         "**/__tests__/*" | ||||||
|  |     ] | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user