homebridge-shinobi
Version:
A Homebridge plugin integrating Shinobi for motion detector cameras
141 lines • 6.84 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ShinobiHomebridgePlatform = void 0;
const fs_1 = __importDefault(require("fs"));
const https_1 = __importDefault(require("https"));
const node_fetch_1 = __importDefault(require("node-fetch"));
const express_1 = __importDefault(require("express"));
const settings_1 = require("./settings");
const shinobiMonitorAccessory_1 = require("./shinobiMonitorAccessory");
/**
* ShinobiHomebridgePlatform
*/
class ShinobiHomebridgePlatform {
constructor(log, config, api) {
this.log = log;
this.config = config;
this.api = api;
this.Service = this.api.hap.Service;
this.Characteristic = this.api.hap.Characteristic;
// this is used to track restored cached accessories
this.existingAccessories = [];
// this is used to have a reference to monitor accessory handles to listen for homebridge shutdown
// and to update state on shinobi webhook callbacks
this.monitorsByMonitorId = new Map();
log.debug('finished initializing platform');
// When this event is fired it means Homebridge has restored all cached accessories from disk.
api.on("didFinishLaunching" /* APIEvent.DID_FINISH_LAUNCHING */, () => {
log.debug('executing didFinishLaunching callback');
this.createMonitors()
.then(() => {
log.debug('monitors created');
}).catch((err) => {
log.error(`failed to create monitors: ${err.message}`);
});
this.startWebhookListener();
});
api.on("shutdown" /* APIEvent.SHUTDOWN */, () => {
log.debug('executed shutdown callback');
this.shutdown();
});
}
/**
* This function is invoked when homebridge restores cached accessories from disk at startup.
* It should be used to setup event handlers for characteristics and update respective values.
*/
configureAccessory(accessory) {
this.log.info(`loading accessory from cache: ${accessory.displayName}`);
// add the restored accessory to the accessories cache so we can track if it has already been registered
this.existingAccessories.push(accessory);
}
/**
* This queries shinobi and uses config to determine which monitor accessories to create.
*/
async createMonitors() {
for (let i = 0; i < this.config.monitors.length; i++) {
const monitorConfig = this.config.monitors[i];
const url = `${this.config.shinobi_api}/${this.config.api_key}/monitor/${this.config.group_key}/${monitorConfig.monitor_id}`;
this.log.debug(`fetching from Shinobi API: ${url}`);
(0, node_fetch_1.default)(url)
.then(res => res.json())
.then(shinobiConfig => {
return {
displayName: `${monitorConfig.monitor_id} monitor`,
useSubStream: monitorConfig.use_substream === true,
monitorConfig,
shinobiConfig
};
})
.then((monitor) => {
this.createMonitor(monitor);
})
.catch(err => {
this.log.error(err);
this.log.error(`didFinishLaunching() error: ${err.message}`);
});
}
}
createMonitor(monitor) {
this.log.debug('createMonitor()');
const monitorId = monitor.monitorConfig.monitor_id;
this.log.debug(`processing monitor: ${monitorId}`);
const uuid = this.api.hap.uuid.generate(`${this.config.group_key}-${monitorId}`);
// see if an accessory with the same uuid has already been registered and restored from
// the cached devices we stored in the `configureAccessory` method above
const existingAccessory = this.existingAccessories.find(accessory => accessory.UUID === uuid);
if (existingAccessory) {
// the accessory already exists
this.log.info(`found existing accessory for UUID: ${uuid} => ${existingAccessory.displayName}`);
// create the accessory handler for the restored accessory
this.monitorsByMonitorId.set(monitorId, new shinobiMonitorAccessory_1.ShinobiMonitorAccessory(this, existingAccessory, monitor, this.config));
}
else {
// the accessory does not yet exist, so we need to create it
this.log.info(`adding new accessory: ${monitor.displayName}`);
// create a new accessory
const accessory = new this.api.platformAccessory(monitor.displayName, uuid);
// create the accessory handler for the newly created accessory
this.monitorsByMonitorId.set(monitorId, new shinobiMonitorAccessory_1.ShinobiMonitorAccessory(this, accessory, monitor, this.config));
// link the accessory to your platform
this.api.registerPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, [accessory]);
}
}
startWebhookListener() {
const app = (0, express_1.default)();
app.get('/', (request, response) => {
const monitorId = request.query.mid;
const group = request.query.group;
this.log.debug(`shinobi motion webhook: group = ${group}, monitorId = ${monitorId}`);
const monitor = this.monitorsByMonitorId.get(monitorId);
if ((this.config.group_key === group) && monitor) {
monitor.setMotionDetected(true);
response.sendStatus(200);
}
else {
response.sendStatus(400);
}
});
if (this.config.https_key_path && this.config.https_cert_path) {
const options = {
key: fs_1.default.readFileSync(this.config.https_key_path),
cert: fs_1.default.readFileSync(this.config.https_cert_path)
};
https_1.default.createServer(options, app).listen(this.config.web_hook_port);
this.log.info(`started HTTPS server for ${settings_1.PLATFORM_NAME} webhooks on port '${this.config.web_hook_port}'`);
}
else {
app.listen(this.config.web_hook_port);
this.log.info(`started HTTP server for ${settings_1.PLATFORM_NAME} webhooks on port '${this.config.web_hook_port}'`);
}
}
shutdown() {
this.monitorsByMonitorId.forEach((monitorAccessory) => {
monitorAccessory.shutdown();
});
}
}
exports.ShinobiHomebridgePlatform = ShinobiHomebridgePlatform;
//# sourceMappingURL=platform.js.map