UNPKG

homebridge-plugin-wrapper

Version:

Wrapper for Homebridge and NodeJS-HAP with reduced dependencies that allows to intercept plugin values and also send to them

243 lines 10.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ControllerStorage = void 0; var tslib_1 = require("tslib"); var util_1 = (0, tslib_1.__importDefault)(require("util")); var debug_1 = (0, tslib_1.__importDefault)(require("debug")); var HAPStorage_1 = require("./HAPStorage"); var debug = (0, debug_1.default)("HAP-NodeJS:ControllerStorage"); var ControllerStorage = /** @class */ (function () { function ControllerStorage(accessory) { this.initialized = false; this.fileCreated = false; this.purgeUnidentifiedAccessoryData = true; // --------------------------------------------------------- this.trackedControllers = []; // used to track controllers before data was loaded from disk this.controllerData = {}; this.accessoryUUID = accessory.UUID; } ControllerStorage.prototype.enqueueSaveRequest = function (timeout) { var _this = this; var _a; if (timeout === void 0) { timeout = 0; } if (this.parent) { this.parent.enqueueSaveRequest(timeout); return; } var plannedTime = Date.now() + timeout; if (this.queuedSaveTimeout) { if (plannedTime <= ((_a = this.queuedSaveTime) !== null && _a !== void 0 ? _a : 0)) { return; } clearTimeout(this.queuedSaveTimeout); } this.queuedSaveTimeout = setTimeout(function () { _this.queuedSaveTimeout = _this.queuedSaveTime = undefined; _this.save(); }, timeout).unref(); this.queuedSaveTime = Date.now() + timeout; }; /** * Links a bridged accessory to the ControllerStorage of the bridge accessory. * * @param accessory */ ControllerStorage.prototype.linkAccessory = function (accessory) { if (!this.linkedAccessories) { this.linkedAccessories = []; } var storage = accessory.controllerStorage; this.linkedAccessories.push(storage); storage.parent = this; var saved = this.restoredAccessories && this.restoredAccessories[accessory.UUID]; if (this.initialized) { storage.init(saved); } }; ControllerStorage.prototype.trackController = function (controller) { controller.setupStateChangeDelegate(this.handleStateChange.bind(this, controller)); // setup delegate if (!this.initialized) { // track controller if data isn't loaded yet this.trackedControllers.push(controller); } else { this.restoreController(controller); } }; ControllerStorage.prototype.untrackController = function (controller) { var index = this.trackedControllers.indexOf(controller); if (index !== -1) { // remove from trackedControllers if storage wasn't initialized yet this.trackedControllers.splice(index, 1); } controller.setupStateChangeDelegate(undefined); // remove association with this storage object this.purgeControllerData(controller); }; ControllerStorage.prototype.purgeControllerData = function (controller) { delete this.controllerData[controller.controllerId()]; if (this.initialized) { this.enqueueSaveRequest(100); } }; ControllerStorage.prototype.handleStateChange = function (controller) { var id = controller.controllerId(); var serialized = controller.serialize(); if (!serialized) { // can be undefined when controller wishes to delete data delete this.controllerData[id]; } else { var controllerData = this.controllerData[id]; if (!controllerData) { this.controllerData[id] = { data: serialized, }; } else { controllerData.data = serialized; } } if (this.initialized) { // only save if data was loaded // run save data "async", as handleStateChange call will probably always be caused by a http request // this should improve our response time this.enqueueSaveRequest(100); } }; ControllerStorage.prototype.restoreController = function (controller) { if (!this.initialized) { throw new Error("Illegal state. Controller data wasn't loaded yet!"); } var controllerData = this.controllerData[controller.controllerId()]; if (controllerData) { try { controller.deserialize(controllerData.data); } catch (error) { console.warn("Could not initialize controller of type '".concat(controller.controllerId(), "' from data stored on disk. Resetting to default: ").concat(error.stack)); controller.handleFactoryReset(); } controllerData.purgeOnNextLoad = undefined; } }; /** * Called when this particular Storage object is feed with data loaded from disk. * This method is only called once. * * @param data - array of {@link StoredControllerData}. undefined if nothing was stored on disk for this particular storage object */ ControllerStorage.prototype.init = function (data) { var _this = this; if (this.initialized) { throw new Error("ControllerStorage for accessory ".concat(this.accessoryUUID, " was already initialized!")); } this.initialized = true; // storing data into our local controllerData Record data && data.forEach(function (saved) { return _this.controllerData[saved.type] = saved.controllerData; }); var restoredControllers = []; this.trackedControllers.forEach(function (controller) { _this.restoreController(controller); restoredControllers.push(controller.controllerId()); }); this.trackedControllers.splice(0, this.trackedControllers.length); // clear tracking list var purgedData = false; Object.entries(this.controllerData).forEach(function (_a) { var _b = (0, tslib_1.__read)(_a, 2), id = _b[0], data = _b[1]; if (data.purgeOnNextLoad) { delete _this.controllerData[id]; purgedData = true; return; } if (!restoredControllers.includes(id)) { data.purgeOnNextLoad = true; } }); if (purgedData) { this.enqueueSaveRequest(500); } }; ControllerStorage.prototype.load = function (username) { if (this.username) { throw new Error("ControllerStorage was already loaded!"); } this.username = username; var key = ControllerStorage.persistKey(username); var saved = HAPStorage_1.HAPStorage.storage().getItem(key); var ownData; if (saved) { this.fileCreated = true; ownData = saved.accessories[this.accessoryUUID]; delete saved.accessories[this.accessoryUUID]; } this.init(ownData); if (this.linkedAccessories) { this.linkedAccessories.forEach(function (linkedStorage) { var savedData = saved && saved.accessories[linkedStorage.accessoryUUID]; linkedStorage.init(savedData); if (saved) { delete saved.accessories[linkedStorage.accessoryUUID]; } }); } if (saved && Object.keys(saved.accessories).length > 0) { if (!this.purgeUnidentifiedAccessoryData) { this.restoredAccessories = saved.accessories; // save data for controllers which aren't linked yet } else { debug("Purging unidentified controller data for bridge %s", username); } } }; ControllerStorage.prototype.save = function () { var _a; if (this.parent) { this.parent.save(); return; } if (!this.initialized) { throw new Error("ControllerStorage has not yet been loaded!"); } if (!this.username) { throw new Error("Cannot save controllerData for a storage without a username!"); } var accessories = (_a = {}, _a[this.accessoryUUID] = this.controllerData, _a); if (this.linkedAccessories) { // grab data from all linked storage objects this.linkedAccessories.forEach(function (accessory) { return accessories[accessory.accessoryUUID] = accessory.controllerData; }); } // TODO removed accessories won't ever be deleted? var accessoryData = this.restoredAccessories || {}; Object.entries(accessories).forEach(function (_a) { var _b = (0, tslib_1.__read)(_a, 2), uuid = _b[0], controllerData = _b[1]; var entries = Object.entries(controllerData); if (entries.length > 0) { accessoryData[uuid] = entries.map(function (_a) { var _b = (0, tslib_1.__read)(_a, 2), id = _b[0], data = _b[1]; return ({ type: id, controllerData: data, }); }); } }); var key = ControllerStorage.persistKey(this.username); if (Object.keys(accessoryData).length > 0) { var saved = { accessories: accessoryData, }; this.fileCreated = true; HAPStorage_1.HAPStorage.storage().setItemSync(key, saved); } else if (this.fileCreated) { this.fileCreated = false; HAPStorage_1.HAPStorage.storage().removeItemSync(key); } }; ControllerStorage.persistKey = function (username) { return util_1.default.format("ControllerStorage.%s.json", username.replace(/:/g, "").toUpperCase()); }; ControllerStorage.remove = function (username) { var key = ControllerStorage.persistKey(username); HAPStorage_1.HAPStorage.storage().removeItemSync(key); }; return ControllerStorage; }()); exports.ControllerStorage = ControllerStorage; //# sourceMappingURL=ControllerStorage.js.map