UNPKG

homebridge-plugin-eufy-security

Version:
205 lines 10.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.EufySecurityHomebridgePlatform = void 0; const eufy_node_client_1 = require("eufy-node-client"); const settings_1 = require("./settings"); const doorbell_platform_accessory_1 = require("./doorbell-platform-accessory"); const eufy_types_1 = require("./eufy-types"); const fs_1 = __importDefault(require("fs")); /** * HomebridgePlatform * This class is the main constructor for your plugin, this is where you should * parse the user config and discover/register accessories with Homebridge. */ class EufySecurityHomebridgePlatform { constructor(log, config, api) { this.log = log; this.api = api; this.Service = this.api.hap.Service; this.Characteristic = this.api.hap .Characteristic; // this is used to track restored cached accessories this.accessories = []; this.config = config; // this.log.debug('Config', this.config); this.log.debug('Finished initializing platform:', this.config.name); this.httpService = new eufy_node_client_1.HttpService(this.config.username, this.config.password); // When this event is fired it means Homebridge has restored all cached accessories from disk. // Dynamic Platform plugins should only register new accessories after this event was fired, // in order to ensure they weren't added to homebridge already. This event can also be used // to start discovery of new accessories. this.api.on('didFinishLaunching', async () => { if (this.config.enablePush) { this.log.info('push client enabled'); await this.setupPushClient(); } else { this.log.info('push client disabled'); } log.debug('Executed didFinishLaunching callback'); // run the method to discover / register your devices as accessories this.discoverDevices(); }); } async setupPushClient() { const storagePath = this.api.user.storagePath(); const credentialsPath = `${storagePath}/eufy-security-credentials.json`; let credentials; if (fs_1.default.existsSync(credentialsPath)) { this.log.info('credentials found. reusing them...'); credentials = JSON.parse(fs_1.default.readFileSync(credentialsPath).toString()); } else { // Register push credentials this.log.info('no credentials found. register new...'); const pushService = new eufy_node_client_1.PushRegisterService(); credentials = await pushService.createPushCredentials(); fs_1.default.writeFileSync(credentialsPath, JSON.stringify(credentials)); this.log.info('wait a short time (5sec)...'); await new Promise((r) => setTimeout(r, 5000)); } // Start push client const pushClient = await eufy_node_client_1.PushClient.init({ androidId: credentials.checkinResponse.androidId, securityToken: credentials.checkinResponse.securityToken, }); const fcmToken = credentials.gcmResponse.token; await new Promise((resolve) => { const tHandle = setTimeout(() => { this.log.error('registering a push token timed out'); resolve(true); }, 20000); this.httpService .registerPushToken(fcmToken) .catch((err) => { clearTimeout(tHandle); this.log.error('failed to register push token', err); resolve(true); }) .then(() => { clearTimeout(tHandle); this.log.debug('registered at eufy with:', fcmToken); resolve(true); }); }); setInterval(async () => { try { await this.httpService.pushTokenCheck(); } catch (err) { this.log.warn('failed to confirm push token'); } }, 30 * 1000); pushClient.connect((msg) => { var _a, _b, _c; this.log.debug('push message:', msg); const matchingUuid = this.api.hap.uuid.generate((_a = msg.payload) === null || _a === void 0 ? void 0 : _a.device_sn); const knownAccessory = this.accessories.find((accessory) => accessory.UUID === matchingUuid); const event_type = (_c = (_b = msg.payload) === null || _b === void 0 ? void 0 : _b.payload) === null || _c === void 0 ? void 0 : _c.event_type; if (knownAccessory) { if (event_type === eufy_types_1.MessageTypes.MOTION_DETECTION || event_type === eufy_types_1.MessageTypes.FACE_DETECTION) { // TODO: Implement motion sensor } else if (event_type === eufy_types_1.MessageTypes.PRESS_DOORBELL) { knownAccessory .getService(this.api.hap.Service.Doorbell) .updateCharacteristic(this.api.hap.Characteristic.ProgrammableSwitchEvent, this.api.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS); } } }); } /** * 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.accessories.push(accessory); } /** * This is an example method showing how to register discovered accessories. * Accessories must only be registered once, previously created accessories * must not be registered again to prevent "duplicate UUID" errors. */ async discoverDevices() { var _a, _b, _c; const hubs = await this.httpService.listHubs(); for (const hub of hubs) { this.log.debug(`found hub "${hub.station_name}" (${hub.station_sn}) `); if ((_a = this.config.ignoreHubSns) === null || _a === void 0 ? void 0 : _a.includes(hub.station_sn)) { this.log.debug('ignoring hub ' + hub.station_sn); } } const devices = await this.httpService.listDevices(); for (const device of devices) { const ignoredHub = (_b = this.config.ignoreHubSns) === null || _b === void 0 ? void 0 : _b.includes(device.station_sn); const ignoredDevice = (_c = this.config.ignoreDeviceSns) === null || _c === void 0 ? void 0 : _c.includes(device.device_sn); // eslint-disable-next-line @typescript-eslint/no-unused-vars const { params, member, station_conn, ...strippedDevice } = device; this.log.debug(`found device "${device.device_name}" (${device.device_sn}) ID: ${device.device_id} Model: ${device.device_model} Serial Number: ${device.device_sn} Type: ${device.device_type} Channel: ${device.device_channel} Hub: ${device.station_conn.station_name} (${device.station_sn}) `); this.log.debug(`device dump: ${{ ...strippedDevice, params: params.map((param) => [ param.param_id, param.param_type, param.param_value, ]), }}`); if (ignoredHub) { this.log.debug(`device is part of ignored hub "${device.station_sn}"`); } if (ignoredDevice) { this.log.debug(`device is ignored "${device.device_sn}"`); } if (ignoredHub || ignoredDevice) { continue; } const uuid = this.api.hap.uuid.generate(device.device_sn); const existingAccessory = this.accessories.find((accessory) => accessory.UUID === uuid); // doorbell if ([ eufy_types_1.DeviceType.BATTERY_DOORBELL, eufy_types_1.DeviceType.BATTERY_DOORBELL_2, eufy_types_1.DeviceType.DOORBELL, ].includes(device.device_type)) { if (existingAccessory) { // the accessory already exists this.log.info('Restoring existing accessory from cache:', existingAccessory.displayName); new doorbell_platform_accessory_1.DoorbellPlatformAccessory(this, existingAccessory, device); } else { // the accessory does not yet exist, so we need to create it this.log.info('Adding new accessory:', device.device_name); // create a new accessory const accessory = new this.api.platformAccessory(device.device_name, uuid); // store a copy of the device object in the `accessory.context` // the `context` property can be used to store any data about the accessory you may need accessory.context.device = device; // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` new doorbell_platform_accessory_1.DoorbellPlatformAccessory(this, accessory, device); // link the accessory to your platform this.api.registerPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, [ accessory, ]); } } } // @todo // this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingAccessory]); // this.log.info('Removing existing accessory from cache:', existingAccessory.displayName); } } exports.EufySecurityHomebridgePlatform = EufySecurityHomebridgePlatform; //# sourceMappingURL=platform.js.map