Initial commit
This commit is contained in:
@@ -0,0 +1,363 @@
|
||||
/* 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();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user