UNPKG

signalk-sonoff-ewelink

Version:
1,097 lines (1,096 loc) 49.7 kB
"use strict"; /* * Copyright 2021 Scott Bender <scott@scottbender.net> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports, "__esModule", { value: true }); var camelCase = require('camelcase'); var ewelink = require('ewelink-api-sbender9'); var Zeroconf = require('ewelink-api-sbender9/src/classes/Zeroconf'); var decryptionData = require('ewelink-api-sbender9/src/helpers/ewelink').decryptionData; var path = require('path'); var dnssd = require('dnssd2'); var fs = require('fs'); var mdns; try { mdns = require('mdns'); } catch (err) { } //the registered one var APP_ID = '6dgd7bXbf6pfrqEftpq3JkYeRn9yBQ6y'; var APP_SECRET = 'ouCUhN7oBkkohUrmFy6VlT6c8iP5CYd4'; //const APP_ID = '4s1FXKC9FaGfoqXhmXSJneb3qcm1gOak' //const APP_SECRET = 'oKvCM06gvwkRbfetd6qWRrbC3rFrbIpV' //const APP_ID = 'KOBxGJna5qkk3JLXw3LHLX3wSNiPjAVi' //const APP_SECRET = '4v0sv6X5IM2ASIBiNDj6kGmSfxo40w7n' //const APP_ID = 'oeVkj2lYFGnJu5XUtWisfW4utiN4u9Mq' //const APP_SECRET = '6Nz4n0xA8s8qdxQf2GqurZj2Fs55FUvM' //const APP_ID_WS = 'YzfeftUVcZ6twZw1OoVKPRFYTrGEg01Q' //const APP_SECRET_WS = '4G91qSoboqYO4Y0XJ0LPPKIsq8reHdfa' var APP_ID_WS = APP_ID; var APP_SECRET_WS = APP_SECRET; var pingTime = 25000; //const pingTime = 120000 //const pingTime = 10000 function default_1(app) { var error = app.error; var debug = app.debug; var sentMetaDevices = {}; var lanConnection; var cloudConnection; var anyCloudOnlyDevices; var cloudOnlyByDevice = {}; var socket; var pollInterval; var putsRegistred = {}; var devicesCache; var arpTable = []; var arpTablePath; var props; var browser; var wsTimer; var wsPingInterval; var plugin = { start: function (properties) { props = properties; if (!props.userName || props.userName.length === 0 || !props.password || props.password.length === 0) { app.setPluginError('please configure username and password'); return; } openConnection(); }, stop: function () { if (socket) { socket.close(); socket = undefined; } if (browser) { browser.stop(); browser = undefined; } if (wsTimer) { clearTimeout(wsTimer); wsTimer = undefined; } if (wsPingInterval) { clearInterval(wsPingInterval); wsPingInterval = undefined; } putsRegistred = {}; sentMetaDevices = {}; devicesCache = undefined; cloudOnlyByDevice = {}; }, id: 'signalk-sonoff-ewelink', name: 'Sonoff/eWeLink', description: 'Signal K Plugin For Sonoff/eWeLink devices', schema: function () { var schema = { type: 'object', properties: { userName: { type: 'string', title: 'User Name', description: 'eWeLink User Name' }, password: { type: 'string', title: 'Password', description: 'eWeLink Password' }, region: { type: 'string', title: 'Region', description: 'eWeLink Region (us, eu or cn)', default: 'us' }, lanMode: { type: 'boolean', title: 'Use LAN Mode', default: true }, useMdns: { type: 'boolean', title: 'Use MDNS Package If available', default: false } } }; if (devicesCache) { devicesCache.forEach(function (device) { if (device.params.switches) { var devSchema_1 = { type: 'object', properties: { deviceName: { type: 'string', title: 'Name', default: device.name, readOnly: true }, enabled: { type: 'boolean', title: 'Enabled', default: true }, bankPath: { type: 'string', title: 'Bank Path', default: camelCase(device.name), description: 'Used to generate the path name, ie. electrical.switches.${bankPath}.0.state' }, forceCloudMode: { type: 'boolean', title: 'Force Cloud Mode', default: false, description: 'Use the cloud for this device even if LAN mode is on' } } }; schema.properties["Device ID ".concat(device.deviceid)] = devSchema_1; device.params.switches.forEach(function (sw) { var _a; var name = ((_a = device.tags) === null || _a === void 0 ? void 0 : _a.ck_channel_name) ? device.tags.ck_channel_name[sw.outlet.toString()] : sw.outlet.toString(); devSchema_1.properties["Channel ".concat(sw.outlet)] = { type: 'object', title: "Channel ".concat(sw.outlet + 1), properties: { displayName: { type: 'string', title: 'Display Name (meta)', default: name }, abbrev: { type: 'string', title: 'Abbreviated Name (meta)' }, enabled: { type: 'boolean', title: 'Enabled', default: true }, switchPath: { type: 'string', title: 'Switch Path', default: camelCase(name), description: 'Used to generate the path name, ie. electrical.switches.bank.${switchPath}.state' } } }; }); } else { schema.properties["Device ID ".concat(device.deviceid)] = { type: 'object', properties: { deviceName: { type: 'string', title: 'Name', default: device.name, readOnly: true }, enabled: { type: 'boolean', title: 'Enabled', default: true }, displayName: { type: 'string', title: 'Display Name (meta)', default: device.name }, abbrev: { type: 'string', title: 'Abbreviated Name (meta)' }, switchPath: { type: 'string', title: 'Switch Path', default: camelCase(device.name), description: 'Used to generate the path name, ie electrical.switches.${switchPath}.state' }, forceCloudMode: { type: 'boolean', title: 'Force Cloud Mode', default: false, description: 'Use the cloud for this device even if LAN mode is on' } } }; if (device.uiid == l1Light) { schema.properties["Device ID ".concat(device.deviceid)].properties.includeDYIModes = { title: 'Include DYI Modes in Presets', type: 'boolean', default: false }; schema.properties["Device ID ".concat(device.deviceid)].properties.presets = { title: 'Presets', type: 'array', items: { type: 'object', properties: { name: { type: 'string', title: 'Name' }, colorR: { type: 'number', title: 'Red', default: 255 }, colorG: { type: 'number', title: 'Green', default: 255 }, colorB: { type: 'number', title: 'Blue', default: 255 }, bright: { type: 'number', title: 'Brightness', description: 'Number between 1-100. Set to 0 to preserve current brightness', default: 100 } } } }; } } }); } return schema; }, uiSchema: function () { var uiSchema = { password: { 'ui:widget': 'password' } }; if (devicesCache) { devicesCache.forEach(function (device) { uiSchema["Device ID ".concat(device.deviceid)] = { 'ui:field': 'collapsible', collapse: { field: 'ObjectField', wrapClassName: 'panel-group' } }; }); } return uiSchema; } }; function startLanMode() { return __awaiter(this, void 0, void 0, function () { var deviceCachePath, err_1, err_2; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 7, , 8]); deviceCachePath = path.join(app.getDataDirPath(), 'devices-cache.json'); arpTablePath = path.join(app.getDataDirPath(), 'arp-table.json'); debug('saving device cache...'); _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4 /*yield*/, cloudConnection.saveDevicesCache(deviceCachePath)]; case 2: _a.sent(); return [3 /*break*/, 4]; case 3: err_1 = _a.sent(); error(err_1); return [3 /*break*/, 4]; case 4: if (!!devicesCache) return [3 /*break*/, 6]; return [4 /*yield*/, Zeroconf.loadCachedDevices(deviceCachePath)]; case 5: devicesCache = _a.sent(); if (devicesCache.error) { //error(devicesCache.error) devicesCache = undefined; return [2 /*return*/]; } _a.label = 6; case 6: readArpTabel(); lanConnection = new ewelink({ devicesCache: devicesCache, arpTable: arpTable, APP_ID: APP_ID, APP_SECRET: APP_SECRET }); getDevices(devicesCache, false); debug('starting dnsd browser...'); if (mdns && props.useMdns) { browser = mdns.createBrowser(mdns.tcp('ewelink')); } else { browser = dnssd.Browser(dnssd.tcp('ewelink')); } browser .on('serviceUp', dnsdChanged) .on('serviceChanged', dnsdChanged) .start(); return [3 /*break*/, 8]; case 7: err_2 = _a.sent(); error(err_2); app.setPluginError(err_2.message); return [3 /*break*/, 8]; case 8: return [2 /*return*/]; } }); }); } function openConnection() { return __awaiter(this, void 0, void 0, function () { var res, devices, err_3; return __generator(this, function (_a) { switch (_a.label) { case 0: cloudConnection = new ewelink({ email: props.userName, password: props.password, region: props.region, APP_ID: APP_ID, APP_SECRET: APP_SECRET }); _a.label = 1; case 1: _a.trys.push([1, 6, , 7]); return [4 /*yield*/, cloudConnection.getCredentials()]; case 2: res = _a.sent(); if (!res.error) return [3 /*break*/, 3]; error('error logging in: ' + JSON.stringify(res)); app.setPluginError(res.msg); return [3 /*break*/, 5]; case 3: return [4 /*yield*/, cloudConnection.getDevices()]; case 4: devices = _a.sent(); debug('found devices: %j', devices); devicesCache = devices; getDevices(devices, true); _a.label = 5; case 5: return [3 /*break*/, 7]; case 6: err_3 = _a.sent(); error(err_3); app.setPluginError(err_3.message); return [3 /*break*/, 7]; case 7: if (!props.lanMode) return [3 /*break*/, 9]; return [4 /*yield*/, startLanMode()]; case 8: _a.sent(); _a.label = 9; case 9: if (devicesCache) { devicesCache.forEach(function (device) { var switchProps = getDeviceProps(device); var cloudOnly = (!switchProps || typeof switchProps.enabled === 'undefined' || switchProps.enabled) && (cloudOnlyHardware.indexOf(device.uiid) !== -1 || (switchProps === null || switchProps === void 0 ? void 0 : switchProps.forceCloudMode) === true); cloudOnlyByDevice[device.deviceid] = cloudOnly; }); anyCloudOnlyDevices = devicesCache.find(function (device) { return cloudOnlyByDevice[device.deviceid] === true; }); if (props.lanMode && !anyCloudOnlyDevices) { app.setPluginStatus('Using LAN mode only'); } if (!props.lanMode || anyCloudOnlyDevices) { try { openWebSocket(); } catch (err) { error(err); } } } return [2 /*return*/]; } }); }); } function openWebSocket() { return __awaiter(this, void 0, void 0, function () { var err_4; var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: cloudConnection = new ewelink({ email: props.userName, password: props.password, region: props.region, APP_ID_WS: APP_ID_WS, APP_SECRET_WS: APP_SECRET_WS }); debug('opening cloud web socket...'); _a.label = 1; case 1: _a.trys.push([1, 4, , 5]); return [4 /*yield*/, cloudConnection.getCredentials()]; case 2: _a.sent(); app.setPluginStatus('Connected to Cloud'); return [4 /*yield*/, cloudConnection .openWebSocket(function (data) { if (typeof data === 'string') { debug('ws recv: ' + data); return; } else { debug('ws recv: %j', data); } if (data.params && data.deviceid) { var device = getCachedDevice(data.deviceid); if (device) { var deviceProps = getDeviceProps(device); if (!deviceProps || typeof deviceProps === 'undefined' || deviceProps.enabled) { sendDeltas(device, data.params); } } else { error("unknown device: ".concat(data.deviceid)); } } })]; case 3: socket = _a.sent(); socket.onClose.addListener(function (err) { error('web socket closed: ' + err); wsTimer = setTimeout(function () { wsTimer = undefined; clearInterval(wsPingInterval); wsPingInterval = undefined; openWebSocket(); }, 5000); }); if (wsPingInterval) { clearInterval(wsPingInterval); } wsPingInterval = setInterval(function () { return __awaiter(_this, void 0, void 0, function () { var err_5; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); debug('sending ping...'); return [4 /*yield*/, socket.send('ping')]; case 1: _a.sent(); return [3 /*break*/, 3]; case 2: err_5 = _a.sent(); error(err_5); return [3 /*break*/, 3]; case 3: return [2 /*return*/]; } }); }); }, pingTime); return [3 /*break*/, 5]; case 4: err_4 = _a.sent(); error(err_4); app.setPluginError(err_4.message); wsTimer = setTimeout(function () { wsTimer = undefined; openWebSocket(); }, 5000); return [3 /*break*/, 5]; case 5: return [2 /*return*/]; } }); }); } function propHandler(context, path, value, device, prop, cb, converter) { var _a; if (converter === void 0) { converter = null; } var params = converter ? converter(value) : (_a = {}, _a[prop] = value, _a); cloudConnection .setWSDeviceParams(device.deviceid, params) .then(function (status) { cloudConnection.getWSDeviceStatus(device.deviceid); debug('got status: %j', status); cb({ state: 'COMPLETED', statusCode: status.status === 'ok' ? 200 : 400, message: status.message }); }) .catch(function (err) { error(err); app.setPluginError(err.message); cb({ state: 'COMPLETED', statusCode: 400, message: err.message }); }); return { state: 'PENDING' }; } function registerPutProp(device, prop, converter) { if (converter === void 0) { converter = null; } var propPath = getSwitchPath(device, prop); app.registerPutHandler('vessels.self', propPath, function (context, path, value, cb) { return propHandler(context, path, value, device, prop, cb, converter); }); } function presetHandler(context, path, value, device, cb) { var switchProps = getSwitchProps(device); var preset = switchProps.presets.find(function (preset) { return preset.name == value; }); if (value === 'Unknown' || !preset) { return { state: 'COMPLETED', statusCode: 400, message: "invalid value ".concat(value) }; } else { var params = { colorR: preset.colorR, colorG: preset.colorG, colorB: preset.colorB, switch: 'on' }; if (preset.bright !== 0) { params.bright = preset.bright; } cloudConnection .setWSDeviceParams(device.deviceid, params) .then(function (status) { cloudConnection.getWSDeviceStatus(device.deviceid); debug('got status: %j', status); cb({ state: 'COMPLETED', statusCode: status.status === 'ok' ? 200 : 400, message: status.message }); }) .catch(function (err) { error(err); app.setPluginError(err.message); cb({ state: 'COMPLETED', statusCode: 400, message: err.message }); }); return { state: 'PENDING' }; } } function filterEnabledDevices(devices) { return devices.filter(function (device) { var deviceProps = getDeviceProps(device); return (!deviceProps || typeof deviceProps.enabled === 'undefined' || deviceProps.enabled); }); } function getDevices(devices, doSendDeltas) { filterEnabledDevices(devices).forEach(function (device) { if (device.params && (device.params.switches || device.params.switch)) { if (device.params.switches) { device.params.switches.forEach(function (channel) { var switchProps = getSwitchProps(device, channel.outlet); if (switchProps === null || switchProps === void 0 ? void 0 : switchProps.enabled) { var switchPath = getBankSwitchPath(device, channel.outlet); if (!putsRegistred[switchPath]) { app.registerPutHandler('vessels.self', switchPath, function (context, path, value, cb) { return bankHandler(context, path, value, device, channel.outlet, cb); }); putsRegistred[switchPath] = true; } } }); } else { var switchProps = getSwitchProps(device); if (switchProps === null || switchProps === void 0 ? void 0 : switchProps.enabled) { var switchPath = getSwitchPath(device); if (!putsRegistred[switchPath]) { app.registerPutHandler('vessels.self', switchPath, function (context, path, value, cb) { return switchHandler(context, path, value, device, cb); }); if (device.uiid === l1Light) { registerPutProp(device, 'mode', function (value) { return { mode: Object.values(l1ModeMap).indexOf(value) + 1 }; }); registerPutProp(device, 'colorR'); registerPutProp(device, 'colorG'); registerPutProp(device, 'colorB'); registerPutProp(device, 'dimmingLevel', function (value) { var val = Number((value * 100).toFixed(0)); if (val === 0) { val = 1; } return { bright: val }; }); registerPutProp(device, 'speed'); registerPutProp(device, 'sensitive'); registerPutProp(device, 'light_type'); var switchProps_1 = getSwitchProps(device); if (switchProps_1.presets) { app.registerPutHandler('vessels.self', getSwitchPath(device, 'preset'), function (context, path, value, cb) { return presetHandler(context, path, value, device, cb); }); } putsRegistred[switchPath] = true; } } } } if (doSendDeltas) { sendDeltas(device, device.params); } } }); } function setBankPowerState(device, state, outlet) { return __awaiter(this, void 0, void 0, function () { var stateStr, res; return __generator(this, function (_a) { switch (_a.label) { case 0: stateStr = state ? 'on' : 'off'; if (!(props.lanMode && !cloudOnlyByDevice[device.deviceid])) return [3 /*break*/, 1]; return [2 /*return*/, lanConnection.setDevicePowerState(device.deviceid, stateStr, outlet + 1)]; case 1: return [4 /*yield*/, cloudConnection.setWSDevicePowerState(device.deviceid, stateStr, { channel: outlet + 1 })]; case 2: res = _a.sent(); cloudConnection.getWSDeviceStatus(device.deviceid); return [2 /*return*/, res]; } }); }); } function bankHandler(context, path, value, device, outlet, cb) { var state = value === 1 || value === 'on' || value === 'true' || value === true; setBankPowerState(device, state, outlet) .then(function (status) { debug('got status outlet %d %j: ', outlet, status); cb({ state: 'COMPLETED', statusCode: status.status === 'ok' ? 200 : 400, message: status.message }); }) .catch(function (err) { error(err); app.setPluginError(err.message); cb({ state: 'COMPLETED', statusCode: 400, message: err.message }); }); return { state: 'PENDING' }; } function setPowerState(device, state) { return __awaiter(this, void 0, void 0, function () { var stateStr, res; return __generator(this, function (_a) { switch (_a.label) { case 0: stateStr = state ? 'on' : 'off'; if (!(props.lanMode && !cloudOnlyByDevice[device.deviceid])) return [3 /*break*/, 1]; return [2 /*return*/, lanConnection.setDevicePowerState(device.deviceid, stateStr)]; case 1: return [4 /*yield*/, cloudConnection.setWSDevicePowerState(device.deviceid, stateStr)]; case 2: res = _a.sent(); cloudConnection.getWSDeviceStatus(device.deviceid); return [2 /*return*/, res]; } }); }); } function switchHandler(context, path, value, device, cb) { var state = value === 1 || value === 'on' || value === 'true'; setPowerState(device, state) .then(function (status) { debug('set status: %j', status); cb({ state: 'COMPLETED', statusCode: status.status === 'ok' ? 200 : 400, message: status.message }); }) .catch(function (err) { error(err); app.setPluginError(err.message); cb({ state: 'COMPLETED', statusCode: 400, message: err.message }); }); return { state: 'PENDING' }; } function sendMeta(device) { var meta = []; if (device.params.switches) { device.params.switches.forEach(function (channel) { var switchProps = getSwitchProps(device, channel.outlet); if (!switchProps || typeof switchProps.enabled === 'undefined' || switchProps.enabled) { meta.push({ path: getBankSwitchPath(device, channel.outlet), value: { displayName: (switchProps === null || switchProps === void 0 ? void 0 : switchProps.displayName) || device.name, abbrev: switchProps === null || switchProps === void 0 ? void 0 : switchProps.abbrev, //units: 'bool', order: channel.outlet } }); meta.push({ path: getBankSwitchPath(device, channel.outlet, null), value: { displayName: (switchProps === null || switchProps === void 0 ? void 0 : switchProps.displayName) || device.name, abbrev: switchProps === null || switchProps === void 0 ? void 0 : switchProps.abbrev, order: channel.outlet } }); } }); } else if (device.params.switch) { var switchProps = getSwitchProps(device); if (!switchProps || typeof switchProps.enabled === 'undefined' || switchProps.enabled) { var extras = {}; if (device.uiid === l1Light) { extras = { type: 'dimmer', canDimWhenOff: true }; meta.push({ path: getSwitchPath(device, 'mode'), value: { enum: Object.values(l1ModeMap), possibleValues: Object.values(l1ModeMap).map(function (mode) { return { title: mode, value: mode }; }) } }); if (switchProps.presets) { meta.push({ path: getSwitchPath(device, 'preset'), value: { displayName: (switchProps === null || switchProps === void 0 ? void 0 : switchProps.displayName) || device.name, possibleValues: __spreadArray([], switchProps.presets.map(function (preset) { return { title: preset.name, value: preset.name }; }), true), enum: __spreadArray([], switchProps.presets.map(function (preset) { return preset.name; }), true) } }); } } meta.push({ path: getSwitchPath(device), value: __assign(__assign({}, extras), { displayName: (switchProps === null || switchProps === void 0 ? void 0 : switchProps.displayName) || device.name, abbrev: switchProps === null || switchProps === void 0 ? void 0 : switchProps.abbrev }) }); meta.push({ path: getSwitchPath(device, null), value: __assign(__assign({}, extras), { displayName: (switchProps === null || switchProps === void 0 ? void 0 : switchProps.displayName) || device.name, abbrev: switchProps === null || switchProps === void 0 ? void 0 : switchProps.abbrev }) }); } } if (meta.length) { debug('sending meta: %j', meta); app.handleMessage(plugin.id, { updates: [ { meta: meta } ] }); } } function sendDeltas(device, params) { var values; if (!sentMetaDevices[device.deviceid]) { sendMeta(device); sentMetaDevices[device.deviceid] = true; } device.params = __assign(__assign({}, device.params), params); if (params.switches) { values = params.switches .map(function (channel) { var switchProps = getSwitchProps(device, channel.outlet); if (!switchProps || typeof switchProps.enabled === 'undefined' || switchProps.enabled) { return [ { path: getBankSwitchPath(device, channel.outlet), value: channel.switch === 'on' ? 1 : 0 }, { path: getBankSwitchPath(device, channel.outlet, 'order'), value: channel.outlet } ]; } else { return null; } }) .filter(function (kp) { return kp != null; }); values = [].concat.apply([], values); } else { var switchProps = getSwitchProps(device); if (!switchProps || typeof switchProps.enabled === 'undefined' || switchProps.enabled) { values = []; if (params.switch) { values.push({ path: getSwitchPath(device), value: params.switch === 'on' ? 1 : 0 }); } var addValue = function (key, v) { var val = typeof v !== 'undefined' ? v : params[key]; if (typeof val !== 'undefined') { values.push({ path: getSwitchPath(device, key), value: val }); } }; if (device.uiid === l1Light) { addValue('mode', l1ModeMap[params.mode]); addValue('colorR'); addValue('colorG'); addValue('colorB'); if (typeof params.bright !== 'undefined') { addValue('dimmingLevel', params.bright / 100.0); } addValue('speed'); addValue('sensitive'); addValue('light_type'); if (switchProps.presets) { var preset = switchProps.presets.find(function (preset) { return (device.params.colorR == preset.colorR && device.params.colorG == preset.colorG && device.params.colorB == preset.colorB && (preset.bright === 0 || device.params.bright == preset.bright)); }); values.push({ path: getSwitchPath(device, 'preset'), value: (preset === null || preset === void 0 ? void 0 : preset.name) || 'Unknown' }); } } } } if (values) { app.handleMessage(plugin.id, { updates: [ { values: values } ] }); } } function getCachedDevice(deviceid) { return devicesCache.find(function (device) { return device.deviceid == deviceid; }); } function findArpTableEntry(mac) { return arpTable.find(function (entry) { return entry.mac == mac; }); } function updateIPAddress(deviceid, service) { if (service.addresses && service.addresses.length > 0) { var device = getCachedDevice(deviceid); if (device) { var mac = device.extra.extra.staMac.toLowerCase(); var arp = findArpTableEntry(mac); if (arp) { arp.ip = service.addresses[0]; } else { arpTable.push({ ip: service.addresses[0], mac: mac }); } saveArpTable(); } } } function readArpTabel() { try { var content = fs.readFileSync(arpTablePath); arpTable = JSON.parse(content); } catch (err) { error(err); } } function saveArpTable() { try { fs.writeFileSync(arpTablePath, JSON.stringify(arpTable, null, 2)); } catch (err) { error(err); } } function dnsdChanged(service) { var txt = service.txt || service.txtRecord; if (!txt || !txt.id || !txt.iv) { error('invalid mdns record'); error(JSON.stringify(service, null, 2)); } var deviceid = txt.id; var iv = txt.iv; debug('got dnsd for device %s (id:%s)', service.name, deviceid); var device = getCachedDevice(deviceid); if (!device) { var msg = 'new device found, please restart the plugin'; error(msg); app.setPluginError(msg); return; } updateIPAddress(deviceid, service); try { var info = decryptMessage(txt, device.devicekey, iv); sendDeltas(device, info); } catch (err) { error(err); app.setPluginError('unable to decrypt mdns data'); } } function decryptMessage(msg, deviceKey, iv) { var encoded = ''; for (var i = 1; i < 6; i++) { var part = msg["data".concat(i)]; if (part) { encoded = encoded + part; } } return JSON.parse(decryptionData(encoded, deviceKey, iv)); } function getDeviceProps(device) { return props["Device ID ".concat(device.deviceid)] || {}; } function getSwitchProps(device, channel) { if (channel === void 0) { channel = undefined; } if (device.params.switches) { var bankConfig = props["Device ID ".concat(device.deviceid)] || {}; return bankConfig["Channel ".concat(channel)]; } else { return props["Device ID ".concat(device.deviceid)] || {}; } } function getSwitchPath(device, key) { if (key === void 0) { key = 'state'; } var config = props["Device ID ".concat(device.deviceid)] || {}; return "electrical.switches.".concat(config.switchPath || camelCase(device.name)).concat(key ? '.' + key : ''); } function getBankSwitchPath(device, channel, key) { var _a, _b, _c; if (key === void 0) { key = 'state'; } var bankConfig = props["Device ID ".concat(device.deviceid)] || {}; var path = (_a = bankConfig["Channel ".concat(channel)]) === null || _a === void 0 ? void 0 : _a.switchPath; var cloud = ((_b = device.tags) === null || _b === void 0 ? void 0 : _b.ck_channel_name) ? (_c = device.tags) === null || _c === void 0 ? void 0 : _c.ck_channel_name[channel.toString()] : undefined; if (!path && cloud) { path = camelCase(cloud); } return "electrical.switches.".concat(bankConfig.bankPath || camelCase(device.name), ".").concat(path || channel).concat(key ? '.' + key : ''); } return plugin; } exports.default = default_1; var l1Light = 59; var cloudOnlyHardware = [l1Light]; var l1ModeMap = { 1: 'Colorful', 2: 'Colorful Gradient', 3: 'Colorful Breath', 4: 'DIY Gradient', 5: 'DIY Pulse', 6: 'DIY Breath', 7: 'DIY Strobe', 8: 'RGB Gradient', 9: 'RGB Pulse', 10: 'RGB Breath', 11: 'RBG Strobe', 12: 'Sync to music' };