UNPKG

homey-api

Version:
232 lines (187 loc) 4.82 kB
'use strict'; const EventEmitter = require('../../EventEmitter'); /** * A superclass for all CRUD Items. * @class * @hideconstructor * @memberof HomeyAPIV3 */ class Item extends EventEmitter { static ID = null; // Set by Manager.js /** * The ID of the Item. * @type {string} */ id; /** * The URI of the Item, e.g. `homey:foo:bar`. * @type {string} */ uri; constructor({ id, homey, manager, properties }) { super(); Object.defineProperty(this, 'id', { value: id, enumerable: true, writable: false, }); Object.defineProperty(this, 'uri', { value: `homey:${this.constructor.ID}:${this.id}`, enumerable: true, writable: false, }); Object.defineProperty(this, '__homey', { value: homey, enumerable: false, writable: false, }); Object.defineProperty(this, '__manager', { value: manager, enumerable: false, writable: false, }); Object.defineProperty(this, '__connected', { value: false, enumerable: false, writable: true, }); Object.defineProperty(this, '__lastUpdated', { value: new Date(), enumerable: false, writable: true, }); // Set Properties for (const [key, value] of Object.entries(properties)) { if (key === 'id') continue; Object.defineProperty(this, key, { value, enumerable: true, writable: true, }); } } /** * The Manager of the Item. * @type {HomeyAPIV3.Manager} */ get manager() { return this.__manager; } /** * The Homey of the Item. * @type {HomeyAPIV3} */ get homey() { return this.__homey; } __debug(...props) { this.manager.__debug(`[${this.constructor.name}:${this.id}]`, ...props); } __update(properties) { for (const [key, value] of Object.entries(properties)) { if (key === 'id') continue; this[key] = value; } this.__lastUpdated = new Date(); this.emit('update', properties); } __delete() { this.destroy(); this.emit('delete'); } /** * Connect to this item's Socket.io namespace. */ async connect() { this.__debug('connect'); // If disconnecting, await that first try { // Ensure all microtasks are done first. E.g. if disconnect is called in the same tick as // connect. This way the disconnect is always started first so we can await the disconnect // promise before we try to connect again. await Promise.resolve(); await this.__disconnectPromise; // eslint-disable-next-line no-empty } catch (err) {} this.__connectPromise = Promise.resolve().then(async () => { if (!this.io) { this.io = this.homey.subscribe(this.uri, { onConnect: () => { this.__debug('onConnect'); this.__connected = true; this.onConnect(); }, onDisconnect: () => { this.__debug('onDisconnect'); this.__connected = false; this.onDisconnect(); }, onReconnect: () => { this.__debug('onDisconnect'); this.onReconnect(); }, onEvent: (event, data) => { this.__debug('onEvent', event, data); this.emit(event, data); }, }); } await this.io; }); // Delete the connecting Promise this.__connectPromise .catch(() => {}) .finally(() => { delete this.__connectPromise; }); await this.__connectPromise; } /** * Discconnect from this item's Socket.io namespace. */ async disconnect() { this.__debug('disconnect'); // If connecting, await that first try { await this.__connectPromise; // eslint-disable-next-line no-empty } catch (err) {} this.__disconnectPromise = Promise.resolve().then(async () => { this.__connected = false; if (this.io) { this.io.then(io => io.unsubscribe()).catch(err => this.__debug('Error Disconnecting:', err)); } }); // Delete the disconnecting Promise this.__disconnectPromise .catch(() => {}) .finally(() => { delete this.__disconnectPromise; // Delete this.io so connect can start new connections. delete this.io; }); await this.__disconnectPromise; } onConnect() { // Overload Me } onReconnect() { // Overload Me } onDisconnect() { // Overload Me } destroy() { // Remove all event listeners this.removeAllListeners(); // Disconnect from Socket.io this.disconnect().catch(() => {}); } static transformGet(item) { return item; } static transformSet(item) { return item; } } module.exports = Item;