Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save kiler129/e9306904fe9e1e5ea5e50153301cd208 to your computer and use it in GitHub Desktop.
Save kiler129/e9306904fe9e1e5ea5e50153301cd208 to your computer and use it in GitHub Desktop.
Scrypted WebHook to disable/enable HomeKit via webhook
class DisableEnableHomeKitWebHook implements HttpRequestHandler {
readonly devType = 'Camera';
readonly roomName = 'Inside';
// readonly roomName = 'Outside';
readonly sharedSecret = 'kfjshflhwfiwheifhawleichiewchewiucahewihc';
async onRequest(request: HttpRequest, response: HttpResponse) {
const query = this.getQueryParams(request);
if (!this.verifySecret(query)) {
response.send("Invalid secret");
return;
}
const action = this.getAction(query);
if (action === null) {
response.send("Action not specified");
return;
}
try {
await this.routeAction(action, response);
} catch (error) {
console.error("Routing failed!");
console.error(error);
response.send("Unable to perform action");
}
}
private async routeAction(action: string, response: HttpResponse): Promise<void> {
let changed = 0;
let rspTxt = '-- No response --';
switch (action) {
case "disableInside":
console.log("Disabling inside cameras...");
changed = await this.changeHomeKitState(this.devType, this.roomName, false);
rspTxt = action + ' disabled ' + changed + ' devices';
break;
case "enableInside":
console.log("Enabling inside cameras...");
changed = await this.changeHomeKitState(this.devType, this.roomName, true);
rspTxt = action + ' enabled ' + changed + ' devices';
break;
default:
rspTxt = 'Unknown action "' + action + '"';
break;
}
response.send(rspTxt);
console.log(rspTxt);
console.log('-----------------------');
}
private getAction(query: URLSearchParams): string | null {
if (!query.has('action')) {
console.warn('Got call without action query param');
return null;
}
return query.get('action');
}
private verifySecret(query: URLSearchParams): bool {
if (!query.has('secret')) {
console.warn('Got call without secret query param');
return false;
}
const secret = query.get('secret');
if (secret !== this.sharedSecret) {
console.error('Got call without invalid secret "' + secret + '"');
return false;
}
return true;
}
private getQueryParams(request: HttpRequest): URLSearchParams {
const queryPos = request.url.indexOf('?');
let queryString = '';
if (queryPos === -1) {
console.warn('Got call without query at all');
} else {
queryString = request.url.substring(queryPos);
}
return new URLSearchParams(queryString);
}
private async changeHomeKitState(devType: string, room: string, shouldEnable: boolean): Promise<number> {
const shouldEnableTxt = shouldEnable ? 'enabled' : 'disabled';
const hkMixinId = this.getHomeKitMixinId();
const devices = this.findDevicesInRoom(devType, room);
let changed = 0;
for (const device of devices) {
const devName = devType + ' "' + device.name + '" (id=' + device.id + ')';
const isEnabled = device.mixins.includes(hkMixinId);
const isEnabledTxt = isEnabled ? 'enabled' : 'disabled';
if (shouldEnable === isEnabled) {
console.log(devName + ' is ' + isEnabledTxt + ' and should be ' +
shouldEnableTxt + ' - nothing to do');
continue;
}
++changed;
const systemDev = sdk.systemManager.getDeviceById(device.id);
if (shouldEnable) {
console.log(devName + ' HK will be enabled - pushing ' + hkMixinId + ' mixin');
device.mixins.push(hkMixinId);
} else {
const mixinIndex = device.mixins.indexOf(hkMixinId);
if (mixinIndex === -1) { //this is impossible if the code isn't broken due to .includes()
throw new Error("Mixin " + hkMixinId + ' not found in id=' + devName);
}
console.log(devName + ' HK will be disabled - pulling index ' + mixinIndex);
device.mixins.splice(mixinIndex, 1);
}
await systemDev.setMixins(device.mixins);
}
return changed;
}
private getHomeKitMixinId(): string {
const homeKitDevice = sdk.systemManager.getDeviceByName('HomeKit');
if (typeof homeKitDevice === 'undefined') {
console.error("Cannot find HomeKit device?");
throw new Error("Cannot find HomeKit mixin id");
}
const hkMixinId = homeKitDevice.id;
console.log('HomeKit pseudo-device/mixin ID:' + hkMixinId);
return String(hkMixinId);
}
private findDevicesInRoom(devType:string, roomName:string): { id: string, name: string, mixins: string[] }[] {
const systemState = sdk.systemManager.getSystemState();
let cameras = [];
for (const [id, deviceState] of Object.entries(systemState)) {
//We only care about one type (e.g. cameras), not any other devices
if (!deviceState.interfaces.value.includes(devType)) {
continue;
}
//We only care about devices in one room (e.g. "Inside")
if (deviceState.room.value !== roomName) {
continue;
}
let device = sdk.systemManager.getDeviceById(id);
cameras.push({
id: id,
name: deviceState.providedName.value,
mixins: device.mixins
})
console.log('Found "' + devType + '" device in "' + roomName + '" room id=' + id + ' name=' + deviceState.providedName.value)
}
return cameras;
}
}
endpointManager.getLocalEndpoint(device.nativeId, { insecure: true, public: true })
.then(endpoint => {
console.log('DisableEnableHomeKitWebHook URL ready:', endpoint);
});
export default DisableEnableHomeKitWebHook;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment