Can successfully start an activity on power on.
This commit is contained in:
parent
ea5b7ba054
commit
a7ae259567
@ -2,15 +2,16 @@ import { Activity } from '../Models/Activity';
|
||||
import { Matrix } from '../Models/Matrix';
|
||||
import { IAccessory } from './IAccessory';
|
||||
import callbackify from '../Util/Callbackify';
|
||||
import HarmonyDataProvider from '../DataProviders/HarmonyDataProvider';
|
||||
|
||||
let Service: HAPNodeJS.Service;
|
||||
let Characteristic: HAPNodeJS.Characteristic;
|
||||
let Api: any;
|
||||
|
||||
export interface IControlUnitProps {
|
||||
dataProvider: HarmonyDataProvider,
|
||||
displayName: string,
|
||||
activities: Array<Activity>,
|
||||
matrix: Matrix,
|
||||
api: any,
|
||||
log: any,
|
||||
}
|
||||
@ -34,7 +35,7 @@ export class ControlUnit implements IAccessory {
|
||||
|
||||
//Harmony fields
|
||||
private activities: Array<Activity> = [];
|
||||
private matrix: Matrix;
|
||||
private dataProvider: HarmonyDataProvider;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -50,7 +51,8 @@ export class ControlUnit implements IAccessory {
|
||||
this.name = this.displayName;
|
||||
|
||||
this.activities = props.activities;
|
||||
this.matrix = props.matrix;
|
||||
|
||||
this.dataProvider = props.dataProvider;
|
||||
|
||||
//Configure services
|
||||
this.configureTvService();
|
||||
@ -100,12 +102,19 @@ export class ControlUnit implements IAccessory {
|
||||
//TODO
|
||||
private onSetAccessoryActive = async (value: any) => {
|
||||
this.log(`set active + ${value}`);
|
||||
switch (value) {
|
||||
case 0: this.dataProvider.powerOff(this.name); break;
|
||||
case 1: this.dataProvider.powerOn(this.name, this.activities[0]); break;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO
|
||||
private onGetAccessoryActive = async () => {
|
||||
this.log(`get active`)
|
||||
return Characteristic.Active.Active;
|
||||
|
||||
//@ts-ignore
|
||||
return this.dataProvider.getIsActive ? Characteristic.Active.Active : Characteristic.Active.Inactive
|
||||
// return Characteristic.Active.Active;
|
||||
}
|
||||
|
||||
//TODO
|
||||
|
@ -1,5 +1,275 @@
|
||||
class HarmonyDataProvider {
|
||||
import { Activity } from "../Models/Activity";
|
||||
import { DeviceSetupItem } from "../Models/DeviceSetupItem";
|
||||
import { threadId } from "worker_threads";
|
||||
import { Input, Matrix, Output } from "../Models/Matrix";
|
||||
|
||||
const Harmony = require("harmony-websocket");
|
||||
interface IDevice {
|
||||
id: string,
|
||||
name: string,
|
||||
supportsCommand(commandName: string): boolean,
|
||||
getCommand(commandName: string): string,
|
||||
commands: { [name: string]: string };
|
||||
on: boolean;
|
||||
}
|
||||
|
||||
export default new HarmonyDataProvider();
|
||||
interface IActivityState {
|
||||
currentActivity: Activity
|
||||
}
|
||||
|
||||
interface IHarmonyDataProviderProps {
|
||||
hubAddress: string,
|
||||
log: any,
|
||||
matrix: Matrix
|
||||
}
|
||||
|
||||
class HarmonyDataProvider {
|
||||
private harmony: any;
|
||||
private log: any;
|
||||
private hubAddress: string = "";
|
||||
private connected: boolean = false;
|
||||
|
||||
private devices: { [name: string]: IDevice; } = {};
|
||||
private states: { [controlUnitName: string]: (IActivityState | undefined) } = {};
|
||||
|
||||
private matrix: Matrix;
|
||||
|
||||
constructor(props: IHarmonyDataProviderProps) {
|
||||
this.log = props.log;
|
||||
this.hubAddress = props.hubAddress;
|
||||
this.matrix = props.matrix;
|
||||
|
||||
this.harmony = new Harmony();
|
||||
|
||||
//Listeners
|
||||
this.harmony.on('open', () => {
|
||||
this.log('Hub open');
|
||||
this.connected = true;
|
||||
});
|
||||
this.harmony.on('close', () => {
|
||||
this.log('Hub closed');
|
||||
this.connected = false;
|
||||
});
|
||||
|
||||
this.connect();
|
||||
}
|
||||
|
||||
public powerOn = async (controlUnitName: string, activity: Activity) => {
|
||||
await this.startActivity(controlUnitName, activity);
|
||||
}
|
||||
|
||||
public powerOff = async (controlUnitName: string) => {
|
||||
if (!this.states[controlUnitName]) {
|
||||
return;
|
||||
}
|
||||
//Build potential list of devices to turn off
|
||||
let devicesToTurnOff: Array<IDevice> = this.states[controlUnitName]!.currentActivity.deviceSetupItems
|
||||
.map((value: DeviceSetupItem): IDevice => {
|
||||
return this.getDeviceFromName(value.deviceName);
|
||||
});
|
||||
|
||||
//Resolve device conflicts with other controlUnits
|
||||
devicesToTurnOff = this.sanitizeDeviceList(devicesToTurnOff, controlUnitName);
|
||||
|
||||
//Turn off devices
|
||||
devicesToTurnOff.forEach((device: IDevice) => {
|
||||
this.powerOffDevice(device);
|
||||
});
|
||||
|
||||
this.states[controlUnitName] = undefined;
|
||||
}
|
||||
|
||||
public startActivity = async (controlUnitName: string, activity: Activity) => {
|
||||
let lastActivity: Activity | undefined = undefined;
|
||||
if (this.states[controlUnitName]) {
|
||||
lastActivity = this.states[controlUnitName]!.currentActivity;
|
||||
}
|
||||
|
||||
// this.log(`asdf: ${JSON.stringify(this.devices)}`);
|
||||
//Build potential list of devices to to turn on
|
||||
let devicesToTurnOn: Array<IDevice> = activity.deviceSetupItems.map((value: DeviceSetupItem): IDevice => {
|
||||
return this.getDeviceFromName(value.deviceName);
|
||||
});
|
||||
|
||||
// this.log(`Devices to turn on: ${JSON.stringify(devicesToTurnOn)}`);
|
||||
|
||||
//Resolve device conflicts with other controlUnits
|
||||
devicesToTurnOn = this.sanitizeDeviceList(devicesToTurnOn, controlUnitName);
|
||||
// this.log(`Sanatized devices to turn on: ${JSON.stringify(devicesToTurnOn)}`)
|
||||
|
||||
//Turn on devices
|
||||
await Promise.all(devicesToTurnOn.map(async (device: IDevice) => {
|
||||
this.log(`Turning on device ${device.name}`)
|
||||
await this.powerOnDevice(device);
|
||||
}));
|
||||
|
||||
//Assign correct input
|
||||
await Promise.all(
|
||||
activity.deviceSetupItems.map(async (value: DeviceSetupItem) => {
|
||||
let device: IDevice = this.getDeviceFromName(value.deviceName);
|
||||
|
||||
if (device.supportsCommand(`Input ${value.input}`)) {
|
||||
let command: string = device.getCommand(`Input ${value.input}`);
|
||||
await this.sendCommand(command);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
if (activity.useMatrix) {
|
||||
//get input and output
|
||||
let input: Input = this.matrix.inputs.filter(e => e.inputDevice === activity.controlDeviceId)[0];
|
||||
let output: Output = this.matrix.outputs.filter(e => e.outputDevice === activity.outputDeviceId)[0];
|
||||
|
||||
let inputCommandName: string = `In ${input.inputNumber}`;
|
||||
let outputCommandName: string = `Out ${output.outputLetter}`;
|
||||
|
||||
// this.log(`Matrix input command: ${inputCommandName} Matrix output command: ${outputCommandName}`);
|
||||
|
||||
let matrixDevice: IDevice = this.getDeviceFromName(this.matrix.deviceName);
|
||||
|
||||
//Rout hdmi
|
||||
if (matrixDevice.supportsCommand(inputCommandName) && matrixDevice.supportsCommand(outputCommandName)) {
|
||||
await this.sendCommand(matrixDevice.getCommand(inputCommandName));
|
||||
await this.sendCommand(matrixDevice.getCommand(outputCommandName));
|
||||
}
|
||||
}
|
||||
|
||||
//Build potential list of devices to turn off
|
||||
if (lastActivity) {
|
||||
let devicesToTurnOff: Array<IDevice> = lastActivity.deviceSetupItems.map((value: DeviceSetupItem): IDevice => {
|
||||
return this.getDeviceFromName(value.deviceName);
|
||||
});
|
||||
|
||||
this.log(`Devices to turn off: ${JSON.stringify(devicesToTurnOff)}`);
|
||||
|
||||
//Resolve device conflicts with other controlUnits
|
||||
devicesToTurnOff = this.sanitizeDeviceList(devicesToTurnOff, controlUnitName);
|
||||
|
||||
this.log(`Sanatized devices to turn off: ${JSON.stringify(devicesToTurnOff)}`);
|
||||
|
||||
await Promise.all(
|
||||
//Turn off devices
|
||||
devicesToTurnOff.map(async (device: IDevice) => {
|
||||
await this.powerOffDevice(device);
|
||||
})
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
//Assign current activity
|
||||
this.states[controlUnitName] = { currentActivity: activity };
|
||||
}
|
||||
|
||||
public volumeUp = async (controlUnitName: string) => {
|
||||
|
||||
}
|
||||
|
||||
public volumeDown = async (controlUnitName: string) => {
|
||||
|
||||
}
|
||||
|
||||
public sendKeyPress = async (controlUnitName: string) => {
|
||||
|
||||
}
|
||||
|
||||
public getIsActive(controlUnitName: string): boolean {
|
||||
return this.states[controlUnitName] ? true : false
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to harmony and receive device info
|
||||
*/
|
||||
private connect = async () => {
|
||||
await this.harmony.connect(this.hubAddress);
|
||||
let self = this;
|
||||
|
||||
setTimeout(async function () {
|
||||
if (self.connected) {
|
||||
let devices: any = await self.harmony.getDevices();
|
||||
try {
|
||||
await Promise.all(
|
||||
//Add each to dictionary
|
||||
devices.map(async (dev: any) => {
|
||||
//get commands
|
||||
let commands: { [name: string]: string } = {};
|
||||
let deviceCommands: any = await self.harmony.getDeviceCommands(dev.id);
|
||||
deviceCommands.forEach((command: any) => {
|
||||
commands[command.label] = command.action;
|
||||
});
|
||||
|
||||
self.devices[dev.label] = {
|
||||
id: dev.id,
|
||||
name: dev.label,
|
||||
commands: commands,
|
||||
on: false,
|
||||
//Define device methods
|
||||
supportsCommand(commandName: string): boolean {
|
||||
let command = commands[commandName];
|
||||
return (command) ? true : false;
|
||||
},
|
||||
getCommand(commandName: string): string {
|
||||
return commands[commandName];
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
} catch (err) {
|
||||
self.log(`ERROR - error connecting to harmony: ${err}`);
|
||||
}
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
private powerOffDevice = async (device: IDevice) => {
|
||||
let powerOffCommand: string = "Power Off";
|
||||
if (device.supportsCommand(powerOffCommand)) {
|
||||
await this.sendCommand(device.getCommand(powerOffCommand));
|
||||
}
|
||||
}
|
||||
|
||||
private powerOnDevice = async (device: IDevice) => {
|
||||
let powerOnCommand: string = "Power On";
|
||||
if (device.supportsCommand(powerOnCommand)) {
|
||||
await this.sendCommand(device.getCommand(powerOnCommand));
|
||||
}
|
||||
}
|
||||
|
||||
private getDeviceFromName(deviceName: string): IDevice {
|
||||
return this.devices[deviceName];
|
||||
}
|
||||
|
||||
private sanitizeDeviceList(devicesToTurnOn: Array<IDevice>, controlUnitName: string): Array<IDevice> {
|
||||
for (let controlUnitKey in this.states) {
|
||||
//Skip self
|
||||
if (controlUnitKey === controlUnitName) {
|
||||
continue;
|
||||
}
|
||||
let currentOtherState: IActivityState = this.states[controlUnitKey]!;
|
||||
|
||||
if (currentOtherState) {
|
||||
currentOtherState.currentActivity.deviceSetupItems.forEach((value: DeviceSetupItem) => {
|
||||
//there are devices to remove
|
||||
if (devicesToTurnOn.some(e => e.name === value.deviceName)) {
|
||||
let deviceToRemove: IDevice = devicesToTurnOn.filter(i => i.name === value.deviceName)[0];
|
||||
delete devicesToTurnOn[devicesToTurnOn.indexOf(deviceToRemove)];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return devicesToTurnOn;
|
||||
}
|
||||
|
||||
private sendCommand = async (command: string) => {
|
||||
try {
|
||||
let response = await this.harmony.sendCommand(JSON.stringify(command));
|
||||
this.log(`Sent command: ${JSON.stringify(command)} response: ${JSON.stringify(response)}`);
|
||||
} catch (err) {
|
||||
this.log(`ERROR - error sending command to harmony: ${err}`);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default HarmonyDataProvider;
|
@ -48,7 +48,7 @@ export class Activity {
|
||||
/**
|
||||
* The device associated with output.
|
||||
*/
|
||||
public outputDeviceId(): string {
|
||||
public get outputDeviceId(): string {
|
||||
return this._outputDeviceId;
|
||||
};
|
||||
|
||||
@ -59,7 +59,7 @@ export class Activity {
|
||||
return this._displayName;
|
||||
}
|
||||
|
||||
public get deviceSetupItem(): Array<DeviceSetupItem> {
|
||||
public get deviceSetupItems(): Array<DeviceSetupItem> {
|
||||
return this._deviceSetupItems
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
|
||||
export interface IDeviceSetupItemProps {
|
||||
deviceId: string,
|
||||
deviceName: string,
|
||||
input: string
|
||||
}
|
||||
|
||||
@ -8,11 +8,11 @@ export class DeviceSetupItem {
|
||||
private _deviceId: string = "";
|
||||
private _input: string = "";
|
||||
constructor(props: IDeviceSetupItemProps) {
|
||||
this._deviceId = props.deviceId;
|
||||
this._deviceId = props.deviceName;
|
||||
this._input = props.input;
|
||||
}
|
||||
|
||||
public get deviceId() {
|
||||
public get deviceName() {
|
||||
return this._deviceId;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
export interface IMatrixProps {
|
||||
inputs: Array<Input>,
|
||||
outputs: Array<Output>,
|
||||
deviceName: string,
|
||||
}
|
||||
|
||||
export interface Input {
|
||||
@ -16,10 +17,12 @@ export interface Output {
|
||||
export class Matrix {
|
||||
private _inputs: Array<Input> = [];
|
||||
private _outputs: Array<Output> = [];
|
||||
private _deviceName: string;
|
||||
|
||||
constructor(props: IMatrixProps) {
|
||||
this._inputs = props.inputs;
|
||||
this._outputs = props.outputs;
|
||||
this._deviceName = props.deviceName;
|
||||
}
|
||||
|
||||
public get inputs(): Array<Input> {
|
||||
@ -29,4 +32,8 @@ export class Matrix {
|
||||
public get outputs(): Array<Output> {
|
||||
return this._outputs;
|
||||
}
|
||||
|
||||
public get deviceName(): string {
|
||||
return this._deviceName;
|
||||
}
|
||||
}
|
18
src/index.ts
18
src/index.ts
@ -2,6 +2,7 @@ import { ControlUnit } from "./Accessories/ControlUnit";
|
||||
import { Activity } from "./Models/Activity";
|
||||
import { DeviceSetupItem } from "./Models/DeviceSetupItem";
|
||||
import { Input, Output, Matrix } from "./Models/Matrix";
|
||||
import HarmonyDataProvider from "./DataProviders/HarmonyDataProvider";
|
||||
|
||||
export default function (homebridge: any) {
|
||||
homebridge.registerPlatform(
|
||||
@ -29,9 +30,14 @@ class HarmonyMatrixPlatform {
|
||||
* @param callback
|
||||
*/
|
||||
accessories(callback: (accessories: Array<ControlUnit>) => void) {
|
||||
//Parse ip
|
||||
let hubIp: string = this.config["hubIp"];
|
||||
|
||||
//Parse matrix
|
||||
let configInputs: any = this.config["Matrix"]["Inputs"];
|
||||
let configOutputs: any = this.config["Matrix"]["Outputs"];
|
||||
let matrixName: string = this.config["Matrix"]["DeviceName"];
|
||||
|
||||
let inputs: Array<Input> = [];
|
||||
let outputs: Array<Output> = [];
|
||||
|
||||
@ -62,6 +68,14 @@ class HarmonyMatrixPlatform {
|
||||
let matrix = new Matrix({
|
||||
inputs: inputs,
|
||||
outputs: outputs,
|
||||
deviceName: matrixName,
|
||||
});
|
||||
|
||||
//construct data provider
|
||||
let dataProvider = new HarmonyDataProvider({
|
||||
hubAddress: hubIp,
|
||||
matrix: matrix,
|
||||
log: this.log,
|
||||
});
|
||||
|
||||
//Parse control units
|
||||
@ -78,7 +92,7 @@ class HarmonyMatrixPlatform {
|
||||
configDevices.forEach((configDevice: any) => {
|
||||
//Add device
|
||||
devices.push(new DeviceSetupItem({
|
||||
deviceId: configDevice["DeviceName"],
|
||||
deviceName: configDevice["DeviceName"],
|
||||
input: configDevice["Input"]
|
||||
}));
|
||||
this.log(`INFO - Added device '${configDevice["DeviceName"]}' for activity '${configActivity["DisplayName"]}'`);
|
||||
@ -98,11 +112,11 @@ class HarmonyMatrixPlatform {
|
||||
|
||||
//Add control unit
|
||||
controlUnits.push(new ControlUnit({
|
||||
dataProvider: dataProvider,
|
||||
displayName: configControlUnit["DisplayName"],
|
||||
api: this.api,
|
||||
log: this.log,
|
||||
activities: activities,
|
||||
matrix: matrix,
|
||||
}));
|
||||
this.log(`INFO - Added ControlUnit`);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user