/* jshint -W097 */// jshint strict:false /*jslint node: true */ 'use strict'; const adapterName = require(__dirname + '/package.json').name.split('.').pop(); const utils = require(__dirname + '/lib/utils'); // Get common adapter utils const tools = require(utils.controllerDir + '/lib/tools.js'); const SocketIO = require(__dirname + '/lib/socket'); const Web = require(__dirname + '/lib/web'); let socket = null; let webServer = null; let objects = {}; let states = {}; let secret = 'Zgfr56gFe87jJOM'; // Will be generated by first start let adapter = new utils.Adapter({ name: adapterName, // adapter name dirname: __dirname, // say own position logTransporter: true, // receive the logs systemConfig: true, install: callback => typeof callback === 'function' && callback() }); adapter.on('objectChange', (id, obj) => { if (obj) { //console.log('objectChange: ' + id); objects[id] = obj; if (id === 'system.repositories') { writeUpdateInfo(); } } else { //console.log('objectDeleted: ' + id); if (objects[id]) { delete objects[id]; } } // TODO Build in some threshold of messages if (socket) { socket.objectChange(id, obj); } }); adapter.on('stateChange', (id, state) => { if (!state) { if (states[id]) { delete states[id]; } } else { states[id] = state; } if (socket) { socket.stateChange(id, state); } }); adapter.on('ready', () => { adapter.getForeignObject('system.config', (err, obj) => { if (!err && obj) { obj.native = obj.native || {}; if (!obj.native.secret) { require('crypto').randomBytes(24, (ex, buf) => { adapter.config.secret = buf.toString('hex'); adapter.extendForeignObject('system.config', {native: {secret: adapter.config.secret}}); main(); }); } else { adapter.config.secret = obj.native.secret; main(); } } else { adapter.config.secret = secret; adapter.logger.error('Cannot find object system.config'); } }); }); adapter.on('message', obj => { if (!obj || !obj.message) { return false; } if (socket) { socket.sendCommand(obj); } return true; }); adapter.on('unload', callback => { if (socket) { // unsubscribe all socket.unsubscribeAll(); } try { adapter.log.info('terminating http' + (adapter.config.secure ? 's' : '') + ' server on port ' + adapter.config.port); webServer.close(); callback(); } catch (e) { callback(); } }); // obj = {message: msg, severity: level, from: this.namespace, ts: (new Date()).getTime()} adapter.on('log', obj => socket && socket.sendLog(obj)); function createUpdateInfo() { // create connected object and state let obj = objects[adapter.namespace + '.info.updatesNumber']; if (!obj || !obj.common || obj.common.type !== 'number') { obj = { _id: 'info.updatesNumber', type: 'state', common: { role: 'indicator.updates', name: 'Number of adapters to update', type: 'number', read: true, write: false, def: 0 }, native: {} }; adapter.setObject(obj._id, obj); } obj = objects[adapter.namespace + '.info.updatesList']; if (!obj || !obj.common || obj.common.type !== 'string') { obj = { _id: 'info.updatesList', type: 'state', common: { role: 'indicator.updates', name: 'List of adapters to update', type: 'string', read: true, write: false, def: '' }, native: {} }; adapter.setObject(obj._id, obj); } } // Helper methods function upToDate(a, b) { a = a.split('.'); b = b.split('.'); a[0] = parseInt(a[0], 10); b[0] = parseInt(b[0], 10); if (a[0] > b[0]) { return false; } else if (a[0] < b[0]) { return true; } else if (a[0] === b[0]) { a[1] = parseInt(a[1], 10); b[1] = parseInt(b[1], 10); if (a[1] > b[1]) { return false; } else if (a[1] < b[1]) { return true; } else if (a[1] === b[1]) { a[2] = parseInt(a[2], 10); b[2] = parseInt(b[2], 10); return a[2] <= b[2]; } } else { return true; } } function writeUpdateInfo(sources) { if (!sources) { let obj = objects['system.repositories']; if (!objects['system.config'] || !objects['system.config'].common) { adapter.log.warn('Repository cannot be read. Invalid "system.config" object.'); return; } const activeRepo = objects['system.config'].common.activeRepo; if (obj && obj.native && obj.native.repositories && obj.native.repositories[activeRepo] && obj.native.repositories[activeRepo].json) { sources = obj.native.repositories[activeRepo].json; } else { adapter.setState('info.updatesNumber', 0, true); adapter.setState('info.updatesList', '', true); if (obj && obj.native && obj.native.repositories && obj.native.repositories[activeRepo]) { adapter.log.warn('Repository cannot be read'); } else { adapter.log.warn('No repository source configured'); } return; } } let installed = tools.getInstalledInfo(); let list = []; for (let name in sources) { if (!sources.hasOwnProperty(name)) continue; if (installed[name] && installed[name].version && sources[name].version) { if (sources[name].version !== installed[name].version && !upToDate(sources[name].version, installed[name].version)) { // remove first part of the name const n = name.indexOf('.'); list.push(n === -1 ? name : name.substring(n + 1)); } } } adapter.setState('info.updatesNumber', list.length, true); adapter.setState('info.updatesList', list.join(', '), true); } // to do => remove it later, when all repositories patched. function patchRepos(callback) { return callback && callback(); // do not patch any more. Delete it later 2018.04.23 /* adapter.getForeignObject('system.repositories', (err, obj) => { let changed = false; if (obj && obj.native && obj.native.repositories) { // default link should point to stable if (!obj.native.repositories.default || obj.native.repositories.default.link !== 'http://download.yunkong2.net/sources-dist.json') { changed = true; obj.native.repositories.default = { link: 'http://download.yunkong2.net/sources-dist.json' }; } // latest link should point to latest if (!obj.native.repositories.latest) { obj.native.repositories.latest = { link: 'http://download.yunkong2.net/sources-dist-latest.json' }; changed = true; } // change URL of raw sources from yunkong2.js-controller to yunkong2.repositories for (let r in obj.native.repositories) { if (obj.native.repositories.hasOwnProperty(r) && obj.native.repositories[r].link === 'https://raw.githubusercontent.com/yunkong2/yunkong2.js-controller/master/conf/sources-dist.json') { obj.native.repositories[r].link = 'https://raw.githubusercontent.com/yunkong2/yunkong2.repositories/master/sources-dist.json'; changed = true; } } } if (changed) { adapter.setForeignObject(obj._id, obj, function () { callback && callback(); }); } else { callback && callback(); } });*/ } function initSocket(server, store) { socket = new SocketIO(server, adapter.config, adapter, objects, states, store); socket.subscribe(null, 'objectChange', '*'); } function main() { // adapter.subscribeForeignStates('*'); // adapter.subscribeForeignObjects('*'); adapter.config.defaultUser = adapter.config.defaultUser || 'admin'; if (!adapter.config.defaultUser.match(/^system\.user\./)) { adapter.config.defaultUser = 'system.user.' + adapter.config.defaultUser; } if (adapter.config.secure) { // Load certificates adapter.getCertificates((err, certificates, leConfig) => { adapter.config.certificates = certificates; adapter.config.leConfig = leConfig; getData(() => webServer = new Web(adapter.config, adapter, initSocket)); }); } else { getData(() => webServer = new Web(adapter.config, adapter, initSocket)); } patchRepos(() => { // By default update repository every 24 hours if (adapter.config.autoUpdate === undefined) { adapter.config.autoUpdate = 24; } adapter.config.autoUpdate = parseInt(adapter.config.autoUpdate, 10) || 0; if (adapter.config.autoUpdate) { setInterval(() => updateRegister(), adapter.config.autoUpdate * 3600000); updateRegister(); } }); } function getData(callback) { adapter.log.info('requesting all states'); let tasks = 0; tasks++; adapter.getForeignStates('*', (err, res) => { adapter.log.info('received all states'); states = res; if (!--tasks && callback) callback(); }); adapter.log.info('requesting all objects'); tasks++; adapter.objects.getObjectList({include_docs: true}, (err, res) => { adapter.log.info('received all objects'); res = res.rows; objects = {}; let tmpPath = ''; for (let i = 0; i < res.length; i++) { objects[res[i].doc._id] = res[i].doc; if (res[i].doc.type === 'instance' && res[i].doc.common && res[i].doc.common.tmpPath) { if (tmpPath) { adapter.log.warn('tmpPath has multiple definitions!!'); } tmpPath = res[i].doc.common.tmpPath; } } // Some adapters want access on specified tmp directory if (tmpPath) { adapter.config.tmpPath = tmpPath; adapter.config.tmpPathAllow = true; } createUpdateInfo(); writeUpdateInfo(); if (!--tasks && callback) callback(); }); } // read repository information from active repository function updateRegister() { adapter.log.info('Request actual repository...'); adapter.getForeignObject('system.config', (err, data) => { if (data && data.common) { adapter.sendToHost(adapter.host, 'getRepository', { repo: data.common.activeRepo, update: true }, _repository => { if (_repository === 'permissionError') { adapter.log.error('May not read "getRepository"'); } else { adapter.log.info('Repository received successfully.'); if (socket) { socket.repoUpdated(); } } }); } }); }