Created sequence accessory
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Brandon Watson 2021-06-07 22:10:50 -04:00
parent 1121dbc52c
commit cdab6327f5
3 changed files with 186 additions and 7 deletions

101
src/Accessories/Sequence.ts Normal file
View File

@ -0,0 +1,101 @@
import {
CharacteristicGetCallback,
CharacteristicSetCallback,
PlatformAccessory,
Service,
} from "homebridge";
import HarmonyDataProvider from "../DataProviders/HarmonyDataProvider";
import { ISequence } from "../Models/Config/ISequence";
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.getDeviceFromName(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 async onSwitchSet(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 device.sendCommand(step.DeviceCommand);
} 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);
}
}

View File

@ -1,10 +1,10 @@
export interface ISequence {
name: string;
steps: Array<IStep>;
DisplayName: string;
Steps: Array<IStep>;
}
export interface IStep {
deviceName: string;
deviceCommand: string;
delay: number;
DeviceName?: string;
DeviceCommand?: string;
Delay: number;
}

View File

@ -8,8 +8,10 @@ import {
Service,
} from "homebridge";
import { ControlUnit, DeviceButton } from "./Accessories";
import { Sequence } from "./Accessories/Sequence";
import HarmonyDataProvider from "./DataProviders/HarmonyDataProvider";
import { IConfig, IControlUnit, IDeviceButton } from "./Models/Config";
import { ISequence } from "./Models/Config/ISequence";
import { HarmonyDevice } from "./Models/HarmonyDevice";
import { HarmonyHub } from "./Models/HarmonyHub";
import { PLATFORM_NAME, PLUGIN_NAME } from "./settings";
@ -33,7 +35,6 @@ export class Platform implements DynamicPlatformPlugin {
this.api.on("didFinishLaunching", async () => {
log.debug("Executed didFinishLaunching callback");
this.discoverDevices(dataProvider);
});
this.dataProvider = null;
@ -51,6 +52,9 @@ export class Platform implements DynamicPlatformPlugin {
this.dataProvider.on("Ready", () => {
this.log.info("All hubs connected");
this.discoverControlUnitAccessories(dataProvider);
this.discoverDeviceButtonAccessories(dataProvider);
this.discoverSequenceAccessories(dataProvider);
if (this.config.EmitDevicesOnStartup) {
const hubs = this.dataProvider!.hubs;
Object.values(hubs).forEach((hub: HarmonyHub) => {
@ -78,10 +82,17 @@ export class Platform implements DynamicPlatformPlugin {
public config: IConfig;
public dataProvider: HarmonyDataProvider | null;
public discoverDevices(dataProvider: HarmonyDataProvider) {
/**
* Discover new control unit accessories
* @param dataProvider
*/
private discoverControlUnitAccessories(
dataProvider: HarmonyDataProvider
): 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: " +
@ -106,7 +117,15 @@ export class Platform implements DynamicPlatformPlugin {
console.log("Publishing external accessory: " + uuid);
}
});
}
/**
* Discover new device button accessories
* @param dataProvider
*/
private discoverDeviceButtonAccessories(
dataProvider: HarmonyDataProvider
): 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);
@ -133,6 +152,65 @@ export class Platform implements DynamicPlatformPlugin {
]);
}
});
// Remove old device buttons
for (const accessory of this.accessories) {
if (
this.config.DeviceButtons.filter(
(button) => button.DisplayName === accessory.displayName
).length === 0
) {
this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [
accessory,
]);
}
}
}
/**
* Discover new sequence accessories
* @param dataProvider
*/
public discoverSequenceAccessories(dataProvider: HarmonyDataProvider): 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, dataProvider, 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;
new Sequence(this, accessory, dataProvider, sequence);
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [
accessory,
]);
}
});
// Remove old device buttons
for (const accessory of this.accessories) {
if (
this.config.Sequences.filter(
(sequence) => sequence.DisplayName === accessory.displayName
).length === 0
) {
this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [
accessory,
]);
}
}
}
configureAccessory(accessory: PlatformAccessory<Record<string, any>>): void {