5 Commits

Author SHA1 Message Date
5dc28b2409 Successfully adding sequences
All checks were successful
continuous-integration/drone/push Build is passing
2021-06-09 17:10:49 -04:00
cdab6327f5 Created sequence accessory
All checks were successful
continuous-integration/drone/push Build is passing
2021-06-07 22:10:50 -04:00
1121dbc52c Fixing stateless device buttons to not error out when pressed
All checks were successful
continuous-integration/drone/push Build is passing
2021-06-07 20:51:19 -04:00
bc2208b5cb Squashed commit of the following:
All checks were successful
continuous-integration/drone/push Build is passing
commit bccadde36f8df34ce9bfd96f73f39428af5c4b14
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Mon Jun 7 20:48:46 2021 -0400

    Using correct branch inclusion

commit 93132ab868
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Mon Jun 7 20:40:00 2021 -0400

    excluding publishing on PRs

commit 496dd42cfd
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Mon Jun 7 20:38:37 2021 -0400

    Only publishing master branch

commit 57e82789fb
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Mon Jun 7 20:36:16 2021 -0400

    WIP

commit ecc54ad3c3
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Mon Jun 7 20:35:29 2021 -0400

    WIP

commit 6d3cd651be
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Mon Jun 7 20:30:18 2021 -0400

    Attempting to use drone commit in preid

commit c60387a987
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Mon Jun 7 20:16:46 2021 -0400

    Adding sequence file

commit d8a5e7541d
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Mon Jun 7 20:16:11 2021 -0400

    Prettier

commit 34fcc1df1a
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Mon Jun 7 20:14:33 2021 -0400

    Updating pre-release command

commit 6c8ce21164
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Mon Jun 7 20:03:41 2021 -0400

    Updating drone config

commit b6d9317a1e
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Wed Jun 2 21:58:35 2021 -0400

    Revert "Using email alias"

    This reverts commit d1a3b80293.

commit d1a3b80293
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Wed Jun 2 21:57:22 2021 -0400

    Using email alias

commit 027d1711ad
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Wed Jun 2 21:36:07 2021 -0400

    Removing bash_profile

commit ed06d7ecd0
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Wed Jun 2 21:23:46 2021 -0400

    WIP

commit d3c37b18b9
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Wed Jun 2 21:22:35 2021 -0400

    Updating registry path
2021-06-07 20:49:45 -04:00
845071c274 Squashed commit of the following:
All checks were successful
continuous-integration/drone/push Build is passing
commit 93132ab868
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Mon Jun 7 20:40:00 2021 -0400

    excluding publishing on PRs

commit 496dd42cfd
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Mon Jun 7 20:38:37 2021 -0400

    Only publishing master branch

commit 57e82789fb
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Mon Jun 7 20:36:16 2021 -0400

    WIP

commit ecc54ad3c3
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Mon Jun 7 20:35:29 2021 -0400

    WIP

commit 6d3cd651be
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Mon Jun 7 20:30:18 2021 -0400

    Attempting to use drone commit in preid

commit c60387a987
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Mon Jun 7 20:16:46 2021 -0400

    Adding sequence file

commit d8a5e7541d
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Mon Jun 7 20:16:11 2021 -0400

    Prettier

commit 34fcc1df1a
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Mon Jun 7 20:14:33 2021 -0400

    Updating pre-release command

commit 6c8ce21164
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Mon Jun 7 20:03:41 2021 -0400

    Updating drone config

commit b6d9317a1e
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Wed Jun 2 21:58:35 2021 -0400

    Revert "Using email alias"

    This reverts commit d1a3b80293.

commit d1a3b80293
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Wed Jun 2 21:57:22 2021 -0400

    Using email alias

commit 027d1711ad
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Wed Jun 2 21:36:07 2021 -0400

    Removing bash_profile

commit ed06d7ecd0
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Wed Jun 2 21:23:46 2021 -0400

    WIP

commit d3c37b18b9
Author: Brandon Watson <watsonb8133@gmail.com>
Date:   Wed Jun 2 21:22:35 2021 -0400

    Updating registry path
2021-06-07 20:46:30 -04:00
8 changed files with 274 additions and 56 deletions

View File

@ -18,7 +18,39 @@ steps:
- npm install
- npm run build
- name: publish
- 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: admin
password:
from_secret: npm_password
email: brandon@watsonlabs.net
registry: "http://linuxhost.me:4873/"
when:
event:
exclude:
- tag
- pull_request
branch:
include:
- master
- name: publish tagged version
image: plugins/npm
settings:
username: admin
@ -29,6 +61,8 @@ steps:
when:
event:
- tag
exclude:
- pull_request
notify:
image: drillster/drone-email

View File

@ -1,22 +0,0 @@
#!/bin/bash
remote_user="bmw"
remote_server="linuxhost.me"
deploy_location="/home/bmw/homebridge-harmony-control"
homebridge_location="/var/lib/homebridge/"
#build
tsc --build
#copy files to remote machine
scp -r bin $remote_user@$remote_server:$deploy_location
scp package.json $remote_user@$remote_server:$deploy_location
#install package
ssh -t $remote_user@$remote_server "sudo npm install -g --unsafe-perm $deploy_location"
#restart service
ssh -t
ssh -t $remote_user@$remote_server "sudo systemctl restart homebridge.service"
ssh -t $remote_user@$remote_server "sudo systemctl status homebridge.service"
echo done
exit

View File

@ -3,5 +3,8 @@
{
"path": "."
}
]
],
"settings": {
"editor.tabSize": 2
}
}

View File

@ -75,23 +75,28 @@ export class DeviceButton {
}
//Execute command
if (this._device) {
//change state if stateful
if (this._deviceInfo.IsStateful && this._buttonState != newState) {
this._buttonState = newState;
await this._device.sendCommand(this._deviceInfo.ButtonName);
} else if (!this._deviceInfo.IsStateful) {
//Send the number of configured key presses
for (let i = 0; i < this._deviceInfo.NumberOfKeyPresses; i++) {
await this._device.sendCommand(this._deviceInfo.ButtonName);
}
this._switchService
.getCharacteristic(this._platform.Characteristic.On)
.updateValue(false);
return callback(new Error("Normal Response"));
}
if (!this._device) {
return callback();
}
//change state if stateful
if (this._deviceInfo.IsStateful && this._buttonState != newState) {
this._buttonState = newState;
await this._device.sendCommand(this._deviceInfo.ButtonName);
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._device.sendCommand(this._deviceInfo.ButtonName);
}
this._switchService
.getCharacteristic(this._platform.Characteristic.On)
.updateValue(false);
this._buttonState = false;
return callback();
}
return callback();
};
/**

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

@ -0,0 +1,105 @@
import {
CharacteristicGetCallback,
CharacteristicSetCallback,
CharacteristicValue,
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 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 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

@ -2,19 +2,21 @@ import { IMatrix } from "./IMatrix";
import { IActivity } from "./IActivity";
import { IDeviceButton } from "./IDeviceButton";
import { IDeviceConfig } from "./IDeviceConfig";
import { IHub } from './IHub';
import { IHub } from "./IHub";
import { ISequence } from "./ISequence";
export interface IControlUnit {
DisplayName: string;
Activities: Array<IActivity>;
DisplayName: string;
Activities: Array<IActivity>;
}
export interface IConfig {
hubIp: string;
EmitDevicesOnStartup: boolean;
Matrix: IMatrix;
ControlUnits: Array<IControlUnit>;
DeviceButtons: Array<IDeviceButton>;
Devices: Array<IDeviceConfig>;
Hubs: Array<IHub>;
}
hubIp: string;
EmitDevicesOnStartup: boolean;
Matrix: IMatrix;
ControlUnits: Array<IControlUnit>;
DeviceButtons: Array<IDeviceButton>;
Sequences: Array<ISequence>;
Devices: Array<IDeviceConfig>;
Hubs: Array<IHub>;
}

View File

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

View File

@ -8,11 +8,14 @@ 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";
import { sleep } from "./Util";
export class Platform implements DynamicPlatformPlugin {
constructor(
@ -22,7 +25,7 @@ export class Platform implements DynamicPlatformPlugin {
) {
this.log.debug("Finished initializing platform:", config.name);
this.config = (config as unknown) as IConfig;
this.config = config as unknown as IConfig;
//construct data provider
const dataProvider = new HarmonyDataProvider({
hubs: this.config.Hubs,
@ -31,9 +34,10 @@ export class Platform implements DynamicPlatformPlugin {
log: this.log,
});
let didFinishLaunching = false;
this.api.on("didFinishLaunching", async () => {
log.debug("Executed didFinishLaunching callback");
this.discoverDevices(dataProvider);
didFinishLaunching = true;
});
this.dataProvider = null;
@ -51,6 +55,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) => {
@ -70,18 +77,25 @@ export class Platform implements DynamicPlatformPlugin {
}
public readonly Service: typeof Service = this.api.hap.Service;
public readonly Characteristic: typeof Characteristic = this.api.hap
.Characteristic;
public readonly Characteristic: typeof Characteristic =
this.api.hap.Characteristic;
// this is used to track restored cached accessories
public readonly accessories: PlatformAccessory[] = [];
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 +120,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 +155,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 {