@s1rd4v3/homebridge-tuya-web
Version:
Homebridge plugin for use with Tuya devices (uses Home Assistant Tuya Web API)
420 lines (323 loc) • 12.9 kB
JavaScript
"use strict";
(function (global, factory) {
if (typeof define === "function" && define.amd) {
define(["exports", "homebridge", "lodash.debounce", "../settings", "util", "../helpers/DebouncedPromise", "../errors"], factory);
} else if (typeof exports !== "undefined") {
factory(exports, require("homebridge"), require("lodash.debounce"), require("../settings"), require("util"), require("../helpers/DebouncedPromise"), require("../errors"));
} else {
var mod = {
exports: {}
};
factory(mod.exports, global.homebridge, global.lodash, global.settings, global.util, global.DebouncedPromise, global.errors);
global.undefined = mod.exports;
}
})(void 0, function (exports, _homebridge, _lodash, _settings, _util, _DebouncedPromise, _errors) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.BaseAccessory = undefined;
var _lodash2 = _interopRequireDefault(_lodash);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then(_next, _throw);
}
}
function _asyncToGenerator(fn) {
return function () {
var self = this,
args = arguments;
return new Promise(function (resolve, reject) {
var gen = fn.apply(self, args);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
}
_next(undefined);
});
};
}
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
if (enumerableOnly) symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
if (i % 2) {
ownKeys(Object(source), true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
}
return target;
}
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
class Cache {
constructor() {
this.validUntil = 0;
}
get valid() {
return this.validUntil > Cache.getCurrentEpoch() && this.value !== undefined;
}
set(data) {
this.validUntil = Cache.getCurrentEpoch() + _settings.TUYA_DEVICE_TIMEOUT + 5;
this.value = data;
}
renew() {
var data = this.get(true);
if (data) {
this.set(data);
}
}
merge(data) {
this.value = _objectSpread(_objectSpread({}, this.value), data);
}
/**
*
* @param always - return the cache even if cache is not valid
*/
get() {
var always = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
if (!always && !this.valid) {
return null;
}
return this.value || null;
}
static getCurrentEpoch() {
return Math.ceil(new Date().getTime() / 1000);
}
}
class BaseAccessory {
constructor(platform, homebridgeAccessory, deviceConfig, categoryType) {
this.platform = platform;
this.homebridgeAccessory = homebridgeAccessory;
this.deviceConfig = deviceConfig;
this.categoryType = categoryType;
this.cache = new Cache();
this.updateCallbackList = new Map();
this.debouncedDeviceStateRequest = (0, _lodash2.default)(this.resolveDeviceStateRequest, 500, {
maxWait: 1500
});
this.log = platform.log;
this.deviceId = deviceConfig.id;
this.log.debug('[%s] deviceConfig: %s', this.deviceConfig.name, (0, _util.inspect)(this.deviceConfig));
switch (categoryType) {
case _homebridge.Categories.LIGHTBULB:
this.serviceType = platform.Service.Lightbulb;
break;
case _homebridge.Categories.SWITCH:
this.serviceType = platform.Service.Switch;
break;
case _homebridge.Categories.OUTLET:
this.serviceType = platform.Service.Outlet;
break;
case _homebridge.Categories.FAN:
this.serviceType = platform.Service.Fanv2;
break;
case _homebridge.Categories.WINDOW_COVERING:
this.serviceType = platform.Service.WindowCovering;
break;
default:
this.serviceType = platform.Service.AccessoryInformation;
} // Retrieve existing of create new Bridged Accessory
if (this.homebridgeAccessory) {
this.homebridgeAccessory.controller = this;
if (!this.homebridgeAccessory.context.deviceId) {
this.homebridgeAccessory.context.deviceId = this.deviceConfig.id;
}
this.log.info('Existing Accessory found [Name: %s] [Tuya ID: %s] [HomeBridge ID: %s]', this.homebridgeAccessory.displayName, this.homebridgeAccessory.context.deviceId, this.homebridgeAccessory.UUID);
this.homebridgeAccessory.displayName = this.deviceConfig.name;
} else {
this.homebridgeAccessory = new this.platform.platformAccessory(this.deviceConfig.name, this.platform.generateUUID(this.deviceConfig.id), categoryType);
this.homebridgeAccessory.context.deviceId = this.deviceConfig.id;
this.homebridgeAccessory.controller = this;
this.log.info('Created new Accessory [Name: %s] [Tuya ID: %s] [HomeBridge ID: %s]', this.homebridgeAccessory.displayName, this.homebridgeAccessory.context.deviceId, this.homebridgeAccessory.UUID);
this.platform.registerPlatformAccessory(this.homebridgeAccessory);
} // Create service
this.service = this.homebridgeAccessory.getService(this.serviceType);
if (this.service) {
this.service.setCharacteristic(platform.Characteristic.Name, this.deviceConfig.name);
} else {
this.log.debug('Creating New Service %s', this.deviceConfig.id);
this.service = this.homebridgeAccessory.addService(this.serviceType, this.deviceConfig.name);
}
this.homebridgeAccessory.on('identify', this.onIdentify.bind(this));
}
get name() {
return this.homebridgeAccessory.displayName;
}
setCharacteristic(characteristic, value) {
var _this$service;
var updateHomekit = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
updateHomekit && ((_this$service = this.service) === null || _this$service === void 0 ? void 0 : _this$service.getCharacteristic(characteristic).updateValue(value));
}
onIdentify() {
this.log.info('[IDENTIFY] %s', this.name);
}
cachedValue() {
var always = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
return this.cache.get(always);
}
resolveDeviceStateRequest() {
var _this = this;
return _asyncToGenerator(function* () {
var promise = _this.debouncedDeviceStateRequestPromise;
if (!promise) {
_this.error('Could not find base accessory promise.');
return;
}
_this.debug('Unsetting debouncedDeviceStateRequestPromise');
_this.debouncedDeviceStateRequestPromise = undefined;
var cached = _this.cache.get();
if (cached !== null) {
_this.debug('Resolving resolveDeviceStateRequest from cache');
return promise.resolve(cached);
}
_this.platform.tuyaWebApi.getDeviceState(_this.deviceId).then(data => {
if (data) {
_this.debug('Set device state request cache');
_this.cache.set(data);
}
_this.debug('Resolving resolveDeviceStateRequest from remote');
promise.resolve(data);
}).catch(error => {
if (error instanceof _errors.RatelimitError) {
_this.debug('Renewing cache due to RateLimitError');
var data = _this.cache.get(true);
if (data) {
_this.cache.renew();
return promise.resolve(data);
}
}
promise.reject(error);
});
})();
}
getDeviceState() {
var _this2 = this;
return _asyncToGenerator(function* () {
_this2.debug('Requesting device state');
if (!_this2.debouncedDeviceStateRequestPromise) {
_this2.debug('Creating new debounced promise');
_this2.debouncedDeviceStateRequestPromise = new _DebouncedPromise.DebouncedPromise();
}
_this2.debug('Triggering debouncedDeviceStateRequest');
_this2.debouncedDeviceStateRequest();
return _this2.debouncedDeviceStateRequestPromise.promise;
})();
}
setDeviceState(method, payload, cache) {
var _this3 = this;
return _asyncToGenerator(function* () {
_this3.cache.merge(cache);
return _this3.platform.tuyaWebApi.setDeviceState(_this3.deviceId, method, payload);
})();
}
updateAccessory(device) {
var setCharacteristic = (characteristic, value) => {
var char = accessoryInformationService.getCharacteristic(characteristic) || accessoryInformationService.addCharacteristic(characteristic);
if (char) {
char.setValue(value);
}
};
this.homebridgeAccessory.displayName = device.name;
this.homebridgeAccessory._associatedHAPAccessory.displayName = device.name;
var accessoryInformationService = this.homebridgeAccessory.getService(this.platform.Service.AccessoryInformation) || this.homebridgeAccessory.addService(this.platform.Service.AccessoryInformation);
setCharacteristic(this.platform.Characteristic.Name, device.name);
setCharacteristic(this.platform.Characteristic.SerialNumber, this.deviceConfig.id);
setCharacteristic(this.platform.Characteristic.Manufacturer, _settings.PLUGIN_NAME);
setCharacteristic(this.platform.Characteristic.Model, this.categoryType); // Update device specific state
this.updateState(device.data);
}
updateState(data) {
this.cache.set(data);
for (var [, callback] of this.updateCallbackList) {
if (callback !== null) {
callback(data);
}
}
}
addUpdateCallback(char, callback) {
this.updateCallbackList.set(char, callback);
}
handleError(type, callback) {
return error => {
this.error('[%s] %s', type, error.message);
callback(error);
};
}
shortcutLog(logLevel, message) {
for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
args[_key - 2] = arguments[_key];
}
this.log.log(logLevel, "[%s] - ".concat(message), this.name, ...args);
}
debug(message) {
for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
args[_key2 - 1] = arguments[_key2];
}
this.shortcutLog(_homebridge.LogLevel.DEBUG, message, ...args);
}
info(message) {
for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
args[_key3 - 1] = arguments[_key3];
}
this.shortcutLog(_homebridge.LogLevel.INFO, message, ...args);
}
warn(message) {
for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
args[_key4 - 1] = arguments[_key4];
}
this.shortcutLog(_homebridge.LogLevel.WARN, message, ...args);
}
error(message) {
for (var _len5 = arguments.length, args = new Array(_len5 > 1 ? _len5 - 1 : 0), _key5 = 1; _key5 < _len5; _key5++) {
args[_key5 - 1] = arguments[_key5];
}
this.shortcutLog(_homebridge.LogLevel.ERROR, message, ...args);
}
}
exports.BaseAccessory = BaseAccessory;
});