homey-api
Version:
232 lines (187 loc) • 4.82 kB
JavaScript
'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;