244 lines
7.3 KiB
TypeScript
244 lines
7.3 KiB
TypeScript
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 startLastOrDefaultActivity = async (
|
|
controlUnitName: string,
|
|
defaultActivity: IActivity
|
|
) => {
|
|
const lastActivity = this.getLastActivity(controlUnitName);
|
|
if (!lastActivity) {
|
|
return this.startActivity(controlUnitName, defaultActivity);
|
|
}
|
|
|
|
return this.startActivity(controlUnitName, lastActivity);
|
|
};
|
|
|
|
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.deactivateState(controlUnitName);
|
|
};
|
|
|
|
public getCurrentActivity(controlUnitName: string): IActivity | undefined {
|
|
return this._stateDataProvider.getState(controlUnitName)?.currentActivity;
|
|
}
|
|
|
|
public getLastActivity(controlUnitName: string): IActivity | undefined {
|
|
return this._stateDataProvider.getState(controlUnitName)?.lastActivity;
|
|
}
|
|
|
|
/**
|
|
* Return if a control unit is active
|
|
* @param controlUnitName
|
|
*/
|
|
public getIsActive(controlUnitName: string): boolean {
|
|
const state = this._stateDataProvider.getState(controlUnitName);
|
|
return state != undefined && state.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<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 || !currentOtherState.currentActivity) {
|
|
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
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|