Created
June 12, 2023 04:36
-
-
Save kiler129/e9306904fe9e1e5ea5e50153301cd208 to your computer and use it in GitHub Desktop.
Scrypted WebHook to disable/enable HomeKit via webhook
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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