1079 lines
45 KiB
JavaScript
1079 lines
45 KiB
JavaScript
|
/* jshint -W097 */// jshint strict:false
|
||
|
/*jslint node: true */
|
||
|
'use strict';
|
||
|
var cp = require('child_process');
|
||
|
var utils = require(__dirname + '/lib/utils'); // Get common adapter utils
|
||
|
var path = require('path');
|
||
|
var dataDir = path.normalize(utils.controllerDir + '/' + require(utils.controllerDir + '/lib/tools').getDefaultDataDir());
|
||
|
var fs = require('fs');
|
||
|
var GetHistory = require(__dirname + '/lib/getHistory.js');
|
||
|
var Aggregate = require(__dirname + '/lib/aggregate.js');
|
||
|
|
||
|
var history = {};
|
||
|
var aliasMap = {};
|
||
|
var subscribeAll = false;
|
||
|
var bufferChecker = null;
|
||
|
var tasksStart = [];
|
||
|
var finished = false;
|
||
|
|
||
|
var adapter = new utils.Adapter({
|
||
|
|
||
|
name: 'history',
|
||
|
|
||
|
objectChange: function (id, obj) {
|
||
|
var formerAliasId = aliasMap[id] ? aliasMap[id] : id;
|
||
|
if (obj && obj.common &&
|
||
|
(
|
||
|
// todo remove history sometime (2016.08) - Do not forget object selector in io-package.json
|
||
|
(obj.common.history && obj.common.history[adapter.namespace] && obj.common.history[adapter.namespace].enabled) ||
|
||
|
(obj.common.custom && obj.common.custom[adapter.namespace] && obj.common.custom[adapter.namespace].enabled)
|
||
|
)
|
||
|
) {
|
||
|
var realId = id;
|
||
|
var checkForRemove = true;
|
||
|
if (obj.common.custom && obj.common.custom[adapter.namespace] && obj.common.custom[adapter.namespace].aliasId) {
|
||
|
if (obj.common.custom[adapter.namespace].aliasId !== id) {
|
||
|
aliasMap[id] = obj.common.custom[adapter.namespace].aliasId;
|
||
|
adapter.log.debug('Registered Alias: ' + id + ' --> ' + aliasMap[id]);
|
||
|
id = aliasMap[id];
|
||
|
checkForRemove = false;
|
||
|
}
|
||
|
else {
|
||
|
adapter.log.warn('Ignoring Alias-ID because identical to ID for ' + id);
|
||
|
obj.common.custom[adapter.namespace].aliasId = '';
|
||
|
}
|
||
|
}
|
||
|
if (checkForRemove && aliasMap[id]) {
|
||
|
adapter.log.debug('Removed Alias: ' + id + ' !-> ' + aliasMap[id]);
|
||
|
delete aliasMap[id];
|
||
|
}
|
||
|
|
||
|
var writeNull = !history[id];
|
||
|
var state = history[id] ? history[id].state : null;
|
||
|
var list = history[id] ? history[id].list : null;
|
||
|
var timeout = history[id] ? history[id].timeout : null;
|
||
|
|
||
|
if (!history[formerAliasId] && !subscribeAll) {
|
||
|
// unsubscribe
|
||
|
for (var _id in history) {
|
||
|
if (history.hasOwnProperty(history[_id].realId)) {
|
||
|
adapter.unsubscribeForeignStates(history[_id].realId);
|
||
|
}
|
||
|
}
|
||
|
subscribeAll = true;
|
||
|
adapter.subscribeForeignStates('*');
|
||
|
}
|
||
|
|
||
|
if (history[formerAliasId] && history[formerAliasId].relogTimeout) clearTimeout(history[formerAliasId].relogTimeout);
|
||
|
|
||
|
// todo remove history somewhen (2016.08)
|
||
|
history[id] = obj.common.custom || obj.common.history;
|
||
|
history[id].state = state;
|
||
|
history[id].list = list;
|
||
|
history[id].timeout = timeout;
|
||
|
history[id].realId = realId;
|
||
|
|
||
|
if (!history[id][adapter.namespace].maxLength && history[id][adapter.namespace].maxLength !== '0' && history[id][adapter.namespace].maxLength !== 0) {
|
||
|
history[id][adapter.namespace].maxLength = parseInt(adapter.config.maxLength, 10) || 960;
|
||
|
} else {
|
||
|
history[id][adapter.namespace].maxLength = parseInt(history[id][adapter.namespace].maxLength, 10);
|
||
|
}
|
||
|
if (!history[id][adapter.namespace].retention && history[id][adapter.namespace].retention !== '0' && history[id][adapter.namespace].retention !== 0) {
|
||
|
history[id][adapter.namespace].retention = parseInt(adapter.config.retention, 10) || 0;
|
||
|
} else {
|
||
|
history[id][adapter.namespace].retention = parseInt(history[id][adapter.namespace].retention, 10) || 0;
|
||
|
}
|
||
|
if (!history[id][adapter.namespace].debounce && history[id][adapter.namespace].debounce !== '0' && history[id][adapter.namespace].debounce !== 0) {
|
||
|
history[id][adapter.namespace].debounce = parseInt(adapter.config.debounce, 10) || 1000;
|
||
|
} else {
|
||
|
history[id][adapter.namespace].debounce = parseInt(history[id][adapter.namespace].debounce, 10);
|
||
|
}
|
||
|
history[id][adapter.namespace].changesOnly = history[id][adapter.namespace].changesOnly === 'true' || history[id][adapter.namespace].changesOnly === true;
|
||
|
if (history[id][adapter.namespace].changesRelogInterval !== undefined && history[id][adapter.namespace].changesRelogInterval !== null && history[id][adapter.namespace].changesRelogInterval !== '') {
|
||
|
history[id][adapter.namespace].changesRelogInterval = parseInt(history[id][adapter.namespace].changesRelogInterval, 10) || 0;
|
||
|
} else {
|
||
|
history[id][adapter.namespace].changesRelogInterval = adapter.config.changesRelogInterval;
|
||
|
}
|
||
|
if (history[id][adapter.namespace].changesRelogInterval > 0) {
|
||
|
history[id].relogTimeout = setTimeout(reLogHelper, (history[id][adapter.namespace].changesRelogInterval * 500 * Math.random()) + history[id][adapter.namespace].changesRelogInterval * 500, id);
|
||
|
}
|
||
|
if (history[id][adapter.namespace].changesMinDelta !== undefined && history[id][adapter.namespace].changesMinDelta !== null && history[id][adapter.namespace].changesMinDelta !== '') {
|
||
|
history[id][adapter.namespace].changesMinDelta = parseFloat(history[id][adapter.namespace].changesMinDelta.toString().replace(/,/g, '.')) || 0;
|
||
|
} else {
|
||
|
history[id][adapter.namespace].changesMinDelta = adapter.config.changesMinDelta;
|
||
|
}
|
||
|
|
||
|
|
||
|
// add one day if retention is too small
|
||
|
if (history[id][adapter.namespace].retention && history[id][adapter.namespace].retention <= 604800) {
|
||
|
history[id][adapter.namespace].retention += 86400;
|
||
|
}
|
||
|
if (writeNull && adapter.config.writeNulls) {
|
||
|
writeNulls(id);
|
||
|
}
|
||
|
|
||
|
adapter.log.info('enabled logging of ' + id + ', Alias=' + (id !== realId));
|
||
|
} else {
|
||
|
if (aliasMap[id]) {
|
||
|
adapter.log.debug('Removed Alias: ' + id + ' !-> ' + aliasMap[id]);
|
||
|
delete aliasMap[id];
|
||
|
}
|
||
|
id = formerAliasId;
|
||
|
if (history[id]) {
|
||
|
adapter.log.info('disabled logging of ' + id);
|
||
|
if (history[id].relogTimeout) clearTimeout(history[id].relogTimeout);
|
||
|
if (history[id].timeout) clearTimeout(history[id].timeout);
|
||
|
storeCached(true, id);
|
||
|
delete history[id];
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
stateChange: function (id, state) {
|
||
|
id = aliasMap[id] ? aliasMap[id] : id;
|
||
|
pushHistory(id, state);
|
||
|
},
|
||
|
|
||
|
unload: function (callback) {
|
||
|
finish(callback);
|
||
|
},
|
||
|
|
||
|
ready: function () {
|
||
|
main();
|
||
|
},
|
||
|
|
||
|
message: function (obj) {
|
||
|
processMessage(obj);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
process.on('SIGINT', function () {
|
||
|
if (adapter && adapter.setState) {
|
||
|
finish();
|
||
|
}
|
||
|
});
|
||
|
process.on('SIGTERM', function () {
|
||
|
if (adapter && adapter.setState) {
|
||
|
finish();
|
||
|
}
|
||
|
});
|
||
|
|
||
|
function storeCached(isFinishing, onlyId) {
|
||
|
var now = new Date().getTime();
|
||
|
|
||
|
for (var id in history) {
|
||
|
if (onlyId !== undefined && onlyId !== id) continue;
|
||
|
if (isFinishing) {
|
||
|
if (history[id].skipped) {
|
||
|
history[id].list.push(history[id].skipped);
|
||
|
history[id].skipped = null;
|
||
|
}
|
||
|
if (adapter.config.writeNulls) {
|
||
|
var nullValue = {val: null, ts: now, lc: now, q: 0x40, from: 'system.adapter.' + adapter.namespace};
|
||
|
if (history[id][adapter.namespace].changesOnly && history[id].state && history[id].state !== null) {
|
||
|
var state = Object.assign({}, history[id].state);
|
||
|
state.ts = now;
|
||
|
state.from = 'system.adapter.' + adapter.namespace;
|
||
|
history[id].list.push(state);
|
||
|
nullValue.ts += 1;
|
||
|
nullValue.lc += 1;
|
||
|
}
|
||
|
|
||
|
// terminate values with null to indicate adapter stop.
|
||
|
history[id].list.push(nullValue);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (history[id].list && history[id].list.length) {
|
||
|
adapter.log.debug('Store the rest for ' + id);
|
||
|
appendFile(id, history[id].list);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function finish(callback) {
|
||
|
adapter.unsubscribeForeignStates('*');
|
||
|
if (bufferChecker) {
|
||
|
clearInterval(bufferChecker);
|
||
|
bufferChecker = null;
|
||
|
}
|
||
|
for (var id in history) {
|
||
|
if (history[id].relogTimeout) {
|
||
|
clearTimeout(history[id].relogTimeout);
|
||
|
history[id].relogTimeout = null;
|
||
|
}
|
||
|
if (history[id].timeout) {
|
||
|
clearTimeout(history[id].timeout);
|
||
|
history[id].timeout = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!finished) {
|
||
|
finished = true;
|
||
|
storeCached(true);
|
||
|
}
|
||
|
|
||
|
if (callback) {
|
||
|
callback();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function processMessage(msg) {
|
||
|
if (msg.command === 'getHistory') {
|
||
|
getHistory(msg);
|
||
|
} else if (msg.command === 'storeState') {
|
||
|
storeState(msg);
|
||
|
} else if (msg.command === 'enableHistory') {
|
||
|
enableHistory(msg);
|
||
|
} else if (msg.command === 'disableHistory') {
|
||
|
disableHistory(msg);
|
||
|
} else if (msg.command === 'getEnabledDPs') {
|
||
|
getEnabledDPs(msg);
|
||
|
} else if (msg.command === 'stopInstance') {
|
||
|
finish(function () {
|
||
|
if (msg.callback) {
|
||
|
adapter.sendTo(msg.from, msg.command, 'stopped', msg.callback);
|
||
|
setTimeout(function () {
|
||
|
process.exit(0);
|
||
|
}, 200);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function fixSelector(callback) {
|
||
|
// fix _design/custom object
|
||
|
adapter.getForeignObject('_design/custom', function (err, obj) {
|
||
|
if (!obj || obj.views.state.map.indexOf('common.history') === -1 || obj.views.state.map.indexOf('common.custom') === -1) {
|
||
|
obj = {
|
||
|
_id: '_design/custom',
|
||
|
language: 'javascript',
|
||
|
views: {
|
||
|
state: {
|
||
|
map: 'function(doc) { if (doc.type===\'state\' && (doc.common.custom || doc.common.history)) emit(doc._id, doc.common.custom || doc.common.history) }'
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
adapter.setForeignObject('_design/custom', obj, function (err) {
|
||
|
if (callback) callback(err);
|
||
|
});
|
||
|
} else {
|
||
|
if (callback) callback(err);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function processStartValues() {
|
||
|
if (tasksStart && tasksStart.length) {
|
||
|
var task = tasksStart.shift();
|
||
|
if (history[task.id][adapter.namespace].changesOnly) {
|
||
|
adapter.getForeignState(history[task.id].realId, function (err, state) {
|
||
|
var now = task.now || new Date().getTime();
|
||
|
pushHistory(task.id, {
|
||
|
val: null,
|
||
|
ts: state ? now - 4 : now, // 4ms because of MS-SQL
|
||
|
ack: true,
|
||
|
q: 0x40,
|
||
|
from: 'system.adapter.' + adapter.namespace});
|
||
|
|
||
|
if (state) {
|
||
|
state.ts = now;
|
||
|
state.from = 'system.adapter.' + adapter.namespace;
|
||
|
pushHistory(task.id, state);
|
||
|
}
|
||
|
setTimeout(processStartValues, 0);
|
||
|
});
|
||
|
} else {
|
||
|
pushHistory(task.id, {
|
||
|
val: null,
|
||
|
ts: task.now || new Date().getTime(),
|
||
|
ack: true,
|
||
|
q: 0x40,
|
||
|
from: 'system.adapter.' + adapter.namespace});
|
||
|
setTimeout(processStartValues, 0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function writeNulls(id, now) {
|
||
|
if (!id) {
|
||
|
now = new Date().getTime();
|
||
|
for (var _id in history) {
|
||
|
if (history.hasOwnProperty(_id)) {
|
||
|
writeNulls(_id, now);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
now = now || new Date().getTime();
|
||
|
tasksStart.push({id: id, now: now});
|
||
|
if (tasksStart.length === 1) {
|
||
|
processStartValues();
|
||
|
}
|
||
|
if (history[id][adapter.namespace].changesRelogInterval > 0) {
|
||
|
if (history[id].relogTimeout) clearTimeout(history[id].relogTimeout);
|
||
|
history[id].relogTimeout = setTimeout(reLogHelper, (history[id][adapter.namespace].changesRelogInterval * 500 * Math.random()) + history[id][adapter.namespace].changesRelogInterval * 500, id);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function main() {
|
||
|
adapter.config.storeDir = adapter.config.storeDir || 'history';
|
||
|
adapter.config.storeDir = adapter.config.storeDir.replace(/\\/g, '/');
|
||
|
if (adapter.config.writeNulls === undefined) adapter.config.writeNulls = true;
|
||
|
|
||
|
// remove last "/"
|
||
|
if (adapter.config.storeDir[adapter.config.storeDir.length - 1] === '/') {
|
||
|
adapter.config.storeDir = adapter.config.storeDir.substring(0, adapter.config.storeDir.length - 1);
|
||
|
}
|
||
|
|
||
|
if (adapter.config.storeDir[0] !== '/' && !adapter.config.storeDir.match(/^\w:\//)) {
|
||
|
adapter.config.storeDir = dataDir + adapter.config.storeDir;
|
||
|
}
|
||
|
adapter.config.storeDir += '/';
|
||
|
|
||
|
if (adapter.config.changesRelogInterval !== null && adapter.config.changesRelogInterval !== undefined) {
|
||
|
adapter.config.changesRelogInterval = parseInt(adapter.config.changesRelogInterval, 10);
|
||
|
} else {
|
||
|
adapter.config.changesRelogInterval = 0;
|
||
|
}
|
||
|
|
||
|
if (adapter.config.changesMinDelta !== null && adapter.config.changesMinDelta !== undefined) {
|
||
|
adapter.config.changesMinDelta = parseFloat(adapter.config.changesMinDelta.toString().replace(/,/g, '.'));
|
||
|
} else {
|
||
|
adapter.config.changesMinDelta = 0;
|
||
|
}
|
||
|
|
||
|
// create directory
|
||
|
if (!fs.existsSync(adapter.config.storeDir)) {
|
||
|
fs.mkdirSync(adapter.config.storeDir);
|
||
|
}
|
||
|
|
||
|
fixSelector(function () {
|
||
|
adapter.objects.getObjectView('custom', 'state', {}, function (err, doc) {
|
||
|
var count = 0;
|
||
|
if (doc && doc.rows) {
|
||
|
for (var i = 0, l = doc.rows.length; i < l; i++) {
|
||
|
if (doc.rows[i].value) {
|
||
|
var id = doc.rows[i].id;
|
||
|
var realId = id;
|
||
|
if (doc.rows[i].value[adapter.namespace] && doc.rows[i].value[adapter.namespace].aliasId) {
|
||
|
aliasMap[id] = doc.rows[i].value[adapter.namespace].aliasId;
|
||
|
adapter.log.debug('Found Alias: ' + id + ' --> ' + aliasMap[id]);
|
||
|
id = aliasMap[id];
|
||
|
}
|
||
|
history[id] = doc.rows[i].value;
|
||
|
|
||
|
// todo remove it somewhen (2016.08)
|
||
|
// convert old value
|
||
|
if (history[id].enabled !== undefined) {
|
||
|
history[id] = history[id].enabled ? {'history.0': history[id]} : null;
|
||
|
if (!history[id]) {
|
||
|
delete history[id];
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
if (!history[id][adapter.namespace] || history[id][adapter.namespace].enabled === false) {
|
||
|
delete history[id];
|
||
|
} else {
|
||
|
count++;
|
||
|
adapter.log.info('enabled logging of ' + id + ' (Count=' + count + '), Alias=' + (id !== realId));
|
||
|
if (!history[id][adapter.namespace].maxLength && history[id][adapter.namespace].maxLength !== '0' && history[id][adapter.namespace].maxLength !== 0) {
|
||
|
history[id][adapter.namespace].maxLength = parseInt(adapter.config.maxLength, 10) || 960;
|
||
|
} else {
|
||
|
history[id][adapter.namespace].maxLength = parseInt(history[id][adapter.namespace].maxLength, 10);
|
||
|
}
|
||
|
if (!history[id][adapter.namespace].retention && history[id][adapter.namespace].retention !== '0' && history[id][adapter.namespace].retention !== 0) {
|
||
|
history[id][adapter.namespace].retention = parseInt(adapter.config.retention, 10) || 0;
|
||
|
} else {
|
||
|
history[id][adapter.namespace].retention = parseInt(history[id][adapter.namespace].retention, 10) || 0;
|
||
|
}
|
||
|
if (!history[id][adapter.namespace].debounce && history[id][adapter.namespace].debounce !== '0' && history[id][adapter.namespace].debounce !== 0) {
|
||
|
history[id][adapter.namespace].debounce = parseInt(adapter.config.debounce, 10) || 1000;
|
||
|
} else {
|
||
|
history[id][adapter.namespace].debounce = parseInt(history[id][adapter.namespace].debounce, 10);
|
||
|
}
|
||
|
history[id][adapter.namespace].changesOnly = history[id][adapter.namespace].changesOnly === 'true' || history[id][adapter.namespace].changesOnly === true;
|
||
|
if (history[id][adapter.namespace].changesRelogInterval !== undefined && history[id][adapter.namespace].changesRelogInterval !== null && history[id][adapter.namespace].changesRelogInterval !== '') {
|
||
|
history[id][adapter.namespace].changesRelogInterval = parseInt(history[id][adapter.namespace].changesRelogInterval, 10) || 0;
|
||
|
} else {
|
||
|
history[id][adapter.namespace].changesRelogInterval = adapter.config.changesRelogInterval;
|
||
|
}
|
||
|
if (history[id][adapter.namespace].changesRelogInterval > 0) {
|
||
|
history[id].relogTimeout = setTimeout(reLogHelper, (history[id][adapter.namespace].changesRelogInterval * 500 * Math.random()) + history[id][adapter.namespace].changesRelogInterval * 500, id);
|
||
|
}
|
||
|
if (history[id][adapter.namespace].changesMinDelta !== undefined && history[id][adapter.namespace].changesMinDelta !== null && history[id][adapter.namespace].changesMinDelta !== '') {
|
||
|
history[id][adapter.namespace].changesMinDelta = parseFloat(history[id][adapter.namespace].changesMinDelta) || 0;
|
||
|
} else {
|
||
|
history[id][adapter.namespace].changesMinDelta = adapter.config.changesMinDelta;
|
||
|
}
|
||
|
|
||
|
// add one day if retention is too small
|
||
|
if (history[id][adapter.namespace].retention && history[id][adapter.namespace].retention <= 604800) {
|
||
|
history[id][adapter.namespace].retention += 86400;
|
||
|
}
|
||
|
|
||
|
history[id].realId = realId;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (count < 20) {
|
||
|
for (var _id in history) {
|
||
|
if (history.hasOwnProperty(_id)) {
|
||
|
adapter.subscribeForeignStates(history[_id].realId);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
subscribeAll = true;
|
||
|
adapter.subscribeForeignStates('*');
|
||
|
}
|
||
|
|
||
|
if (adapter.config.writeNulls) writeNulls();
|
||
|
|
||
|
// store all buffered data every 10 minutes to not lost the data
|
||
|
bufferChecker = setInterval(function () {
|
||
|
storeCached();
|
||
|
}, 10 * 60000);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
adapter.subscribeForeignObjects('*');
|
||
|
}
|
||
|
|
||
|
function pushHistory(id, state, timerRelog) {
|
||
|
if (timerRelog === undefined) timerRelog = false;
|
||
|
// Push into history
|
||
|
if (history[id]) {
|
||
|
var settings = history[id][adapter.namespace];
|
||
|
|
||
|
if (!settings || !state) return;
|
||
|
|
||
|
if (state.ts < 946681200000) state.ts *= 1000;
|
||
|
if (state.lc < 946681200000) state.lc *= 1000;
|
||
|
|
||
|
if (typeof state.val === 'string') {
|
||
|
var f = parseFloat(state.val);
|
||
|
if (f == state.val) {
|
||
|
state.val = f;
|
||
|
}
|
||
|
}
|
||
|
if (history[id].state && settings.changesOnly && !timerRelog) {
|
||
|
if (settings.changesRelogInterval === 0) {
|
||
|
if ((history[id].state.val !== null || state.val === null) && state.ts !== state.lc) {
|
||
|
history[id].skipped = state; // remember new timestamp
|
||
|
adapter.log.debug('value not changed ' + id + ', last-value=' + history[id].state.val + ', new-value=' + state.val + ', ts=' + state.ts);
|
||
|
return;
|
||
|
}
|
||
|
} else if (history[id].lastLogTime) {
|
||
|
if ((history[id].state.val !== null || state.val === null) && (state.ts !== state.lc) && (Math.abs(history[id].lastLogTime - state.ts) < settings.changesRelogInterval * 1000)) {
|
||
|
history[id].skipped = state; // remember new timestamp
|
||
|
adapter.log.debug('value not changed ' + id + ', last-value=' + history[id].state.val + ', new-value=' + state.val + ', ts=' + state.ts);
|
||
|
return;
|
||
|
}
|
||
|
if (state.ts !== state.lc) {
|
||
|
adapter.log.debug('value-changed-relog ' + id + ', value=' + state.val + ', lastLogTime=' + history[id].lastLogTime + ', ts=' + state.ts);
|
||
|
}
|
||
|
}
|
||
|
if (history[id].state.val !== null && (settings.changesMinDelta !== 0) && (typeof state.val === 'number') && (Math.abs(history[id].state.val - state.val) < settings.changesMinDelta)) {
|
||
|
history[id].skipped = state; // remember new timestamp
|
||
|
adapter.log.debug('Min-Delta not reached ' + id + ', last-value=' + history[id].state.val + ', new-value=' + state.val + ', ts=' + state.ts);
|
||
|
return;
|
||
|
}
|
||
|
else if (typeof state.val === 'number') {
|
||
|
adapter.log.debug('Min-Delta reached ' + id + ', last-value=' + history[id].state.val + ', new-value=' + state.val + ', ts=' + state.ts);
|
||
|
}
|
||
|
else {
|
||
|
adapter.log.debug('Min-Delta ignored because no number ' + id + ', last-value=' + history[id].state.val + ', new-value=' + state.val + ', ts=' + state.ts);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (history[id].relogTimeout) {
|
||
|
clearTimeout(history[id].relogTimeout);
|
||
|
history[id].relogTimeout = null;
|
||
|
}
|
||
|
if (settings.changesRelogInterval > 0) {
|
||
|
history[id].relogTimeout = setTimeout(reLogHelper, settings.changesRelogInterval * 1000, id);
|
||
|
}
|
||
|
|
||
|
var ignoreDebonce = false;
|
||
|
if (timerRelog) {
|
||
|
state.ts = new Date().getTime();
|
||
|
adapter.log.debug('timed-relog ' + id + ', value=' + state.val + ', lastLogTime=' + history[id].lastLogTime + ', ts=' + state.ts);
|
||
|
ignoreDebonce = true;
|
||
|
} else {
|
||
|
if (settings.changesOnly && history[id].skipped) {
|
||
|
history[id].state = history[id].skipped;
|
||
|
pushHelper(id);
|
||
|
}
|
||
|
if (history[id].state && ((history[id].state.val === null && state.val !== null) || (history[id].state.val !== null && state.val === null))) {
|
||
|
ignoreDebonce = true;
|
||
|
} else if (!history[id].state && state.val === null) {
|
||
|
ignoreDebonce = true;
|
||
|
}
|
||
|
// only store state if really changed
|
||
|
history[id].state = state;
|
||
|
}
|
||
|
history[id].lastLogTime = state.ts;
|
||
|
history[id].skipped = null;
|
||
|
if (settings.debounce && !ignoreDebonce) {
|
||
|
// Discard changes in de-bounce time to store last stable value
|
||
|
if (history[id].timeout) clearTimeout(history[id].timeout);
|
||
|
history[id].timeout = setTimeout(pushHelper, settings.debounce, id);
|
||
|
} else {
|
||
|
pushHelper(id);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function reLogHelper(_id) {
|
||
|
if (!history[_id]) {
|
||
|
adapter.log.info('non-existing id ' + _id);
|
||
|
return;
|
||
|
}
|
||
|
history[_id].relogTimeout = null;
|
||
|
if (history[_id].skipped) {
|
||
|
history[_id].state = history[_id].skipped;
|
||
|
history[_id].state.from = 'system.adapter.' + adapter.namespace;
|
||
|
history[_id].skipped = null;
|
||
|
pushHistory(_id, history[_id].state, true);
|
||
|
}
|
||
|
else {
|
||
|
adapter.getForeignState(history[_id].realId, function (err, state) {
|
||
|
if (err) {
|
||
|
adapter.log.info('init timed Relog: can not get State for ' + _id + ' : ' + err);
|
||
|
}
|
||
|
else if (!state) {
|
||
|
adapter.log.info('init timed Relog: disable relog because state not set so far ' + _id + ': ' + JSON.stringify(state));
|
||
|
}
|
||
|
else {
|
||
|
adapter.log.debug('init timed Relog: getState ' + _id + ': Value=' + state.val + ', ack=' + state.ack + ', ts=' + state.ts + ', lc=' + state.lc);
|
||
|
history[_id].state = state;
|
||
|
pushHistory(_id, history[_id].state, true);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function pushHelper(_id) {
|
||
|
if (!history[_id] || !history[_id].state) return;
|
||
|
var _settings = history[_id][adapter.namespace];
|
||
|
// if it was not deleted in this time
|
||
|
if (_settings) {
|
||
|
history[_id].timeout = null;
|
||
|
history[_id].list = history[_id].list || [];
|
||
|
|
||
|
if (typeof history[_id].state.val === 'string') {
|
||
|
var f = parseFloat(history[_id].state.val);
|
||
|
if (f == history[_id].state.val) {
|
||
|
history[_id].state.val = f;
|
||
|
} else if (history[_id].state.val === 'true') {
|
||
|
history[_id].state.val = true;
|
||
|
} else if (history[_id].state.val === 'false') {
|
||
|
history[_id].state.val = false;
|
||
|
}
|
||
|
}
|
||
|
if (history[_id].state.lc !== undefined) delete history[_id].state.lc;
|
||
|
if (!adapter.config.storeAck && history[_id].state.ack !== undefined) {
|
||
|
delete history[_id].state.ack;
|
||
|
} else {
|
||
|
history[_id].state.ack = history[_id].state.ack ? 1 : 0;
|
||
|
}
|
||
|
if (!adapter.config.storeFrom && history[_id].state.from !== undefined) delete history[_id].state.from;
|
||
|
|
||
|
history[_id].list.push(history[_id].state);
|
||
|
|
||
|
if (history[_id].list.length > _settings.maxLength) {
|
||
|
adapter.log.debug('moving ' + history[_id].list.length + ' entries from '+ _id +' to file');
|
||
|
appendFile(_id, history[_id].list);
|
||
|
checkRetention(_id);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function checkRetention(id) {
|
||
|
if (history[id][adapter.namespace].retention) {
|
||
|
var d = new Date();
|
||
|
var dt = d.getTime();
|
||
|
// check every 6 hours
|
||
|
if (!history[id].lastCheck || dt - history[id].lastCheck >= 21600000/* 6 hours */) {
|
||
|
history[id].lastCheck = dt;
|
||
|
// get list of directories
|
||
|
var dayList = getDirectories(adapter.config.storeDir).sort(function (a, b) {
|
||
|
return a - b;
|
||
|
});
|
||
|
// calculate date
|
||
|
d.setSeconds(-(history[id][adapter.namespace].retention));
|
||
|
var day = GetHistory.ts2day(d.getTime());
|
||
|
for (var i = 0; i < dayList.length; i++) {
|
||
|
if (dayList[i] < day) {
|
||
|
const file = GetHistory.getFilenameForID(adapter.config.storeDir, dayList[i], id);
|
||
|
if (fs.existsSync(file)) {
|
||
|
adapter.log.info('Delete old history "' + file + '"');
|
||
|
try {
|
||
|
fs.unlinkSync(file);
|
||
|
} catch (ex) {
|
||
|
adapter.log.error('Cannot delete file "' + file + '": ' + ex);
|
||
|
}
|
||
|
var files = fs.readdirSync(adapter.config.storeDir + dayList[i]);
|
||
|
if (!files.length) {
|
||
|
adapter.log.info('Delete old history dir "' + adapter.config.storeDir + dayList[i] + '"');
|
||
|
try {
|
||
|
fs.unlinkSync(adapter.config.storeDir + dayList[i]);
|
||
|
} catch (ex) {
|
||
|
adapter.log.error('Cannot delete directory "' + adapter.config.storeDir + dayList[i] + '": ' + ex);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function appendFile(id, states) {
|
||
|
var day = GetHistory.ts2day(states[states.length - 1].ts);
|
||
|
|
||
|
const file = GetHistory.getFilenameForID(adapter.config.storeDir, day, id);
|
||
|
var data;
|
||
|
|
||
|
var i;
|
||
|
for (i = states.length - 1; i >= 0; i--) {
|
||
|
if (!states[i]) continue;
|
||
|
if (GetHistory.ts2day(states[i].ts) !== day) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
data = states.splice(i - states.length + 1);
|
||
|
|
||
|
if (fs.existsSync(file)) {
|
||
|
try {
|
||
|
data = JSON.parse(fs.readFileSync(file)).concat(data);
|
||
|
} catch (err) {
|
||
|
adapter.log.error('Cannot read file ' + file + ': ' + err);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
// create directory
|
||
|
if (!fs.existsSync(adapter.config.storeDir + day)) {
|
||
|
fs.mkdirSync(adapter.config.storeDir + day);
|
||
|
}
|
||
|
fs.writeFileSync(file, JSON.stringify(data, null, 2));
|
||
|
} catch (ex) {
|
||
|
adapter.log.error('Cannot store file ' + file + ': ' + ex);
|
||
|
}
|
||
|
|
||
|
if (states.length) {
|
||
|
appendFile(id, states);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function getOneCachedData(id, options, cache, addId) {
|
||
|
addId = addId || options.addId;
|
||
|
|
||
|
if (history[id]) {
|
||
|
var res = history[id].list;
|
||
|
// todo can be optimized
|
||
|
if (res) {
|
||
|
var iProblemCount = 0;
|
||
|
var vLast = null;
|
||
|
for (var i = res.length - 1; i >= 0 ; i--) {
|
||
|
if (!res[i]) {
|
||
|
iProblemCount++;
|
||
|
continue;
|
||
|
}
|
||
|
if (options.start && res[i].ts < options.start) {
|
||
|
if (options.ack) res[i].ack = !!res[i].ack;
|
||
|
if (addId) res[i].id = id;
|
||
|
// add one before start
|
||
|
cache.unshift(res[i]);
|
||
|
break;
|
||
|
} else if (res[i].ts > options.end) {
|
||
|
// add one after end
|
||
|
vLast = res[i];
|
||
|
continue;
|
||
|
}
|
||
|
if (options.ack) res[i].ack = !!res[i].ack;
|
||
|
|
||
|
if (vLast) {
|
||
|
if (options.ack) vLast.ack = !!vLast.ack;
|
||
|
if (addId) res[i].id = id;
|
||
|
cache.unshift(vLast);
|
||
|
vLast = null;
|
||
|
}
|
||
|
|
||
|
if (addId) res[i].id = id;
|
||
|
cache.unshift(res[i]);
|
||
|
|
||
|
if (!options.start && cache.length >= options.count) break;
|
||
|
}
|
||
|
if (iProblemCount) adapter.log.warn('got null states ' + iProblemCount + ' times for ' + options.id);
|
||
|
|
||
|
adapter.log.debug('got ' + res.length + ' datapoints for ' + options.id);
|
||
|
} else {
|
||
|
adapter.log.debug('datapoints for ' + options.id + ' do not yet exist');
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function getCachedData(options, callback) {
|
||
|
var cache = [];
|
||
|
|
||
|
if (options.id && options.id !== '*') {
|
||
|
getOneCachedData(options.id, options, cache);
|
||
|
} else {
|
||
|
for (var id in history) {
|
||
|
getOneCachedData(id, options, cache, true);
|
||
|
}
|
||
|
}
|
||
|
options.length = cache.length;
|
||
|
callback(cache, !options.start && cache.length >= options.count);
|
||
|
}
|
||
|
|
||
|
function tsSort(a, b) {
|
||
|
return b.ts - a.ts;
|
||
|
}
|
||
|
|
||
|
function getOneFileData(dayList, dayStart, dayEnd, id, options, data, addId) {
|
||
|
addId = addId || options.addId;
|
||
|
|
||
|
// get all files in directory
|
||
|
for (var i = 0; i < dayList.length; i++) {
|
||
|
var day = parseInt(dayList[i], 10);
|
||
|
|
||
|
if (!isNaN(day) && day > 20100101 && day >= dayStart && day <= dayEnd) {
|
||
|
const file = GetHistory.getFilenameForID(options.path, dayList[i], id);
|
||
|
|
||
|
if (fs.existsSync(file)) {
|
||
|
try {
|
||
|
var _data = JSON.parse(fs.readFileSync(file)).sort(tsSort);
|
||
|
var last = false;
|
||
|
|
||
|
for (var ii in _data) {
|
||
|
if (!_data.hasOwnProperty(ii)) continue;
|
||
|
if (options.ack) {
|
||
|
_data[ii].ack = !!_data[ii].ack;
|
||
|
}
|
||
|
if (addId) {
|
||
|
_data[ii].id = id;
|
||
|
}
|
||
|
data.push(_data[ii]);
|
||
|
if (!options.start && data.length >= options.count) break;
|
||
|
if (last) break;
|
||
|
if (options.start && _data[ii].ts < options.start) last = true;
|
||
|
}
|
||
|
} catch (e) {
|
||
|
console.log('Cannot parse file ' + file + ': ' + e.message);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (!options.start && data.length >= options.count) break;
|
||
|
if (day > dayEnd) break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function getFileData(options, callback) {
|
||
|
var dayStart = options.start ? parseInt(GetHistory.ts2day(options.start), 10) : 0;
|
||
|
var dayEnd = parseInt(GetHistory.ts2day(options.end), 10);
|
||
|
var fileData = [];
|
||
|
|
||
|
// get list of directories
|
||
|
var dayList = getDirectories(options.path).sort(function (a, b) {
|
||
|
return b - a;
|
||
|
});
|
||
|
|
||
|
if (options.id && options.id !== '*') {
|
||
|
getOneFileData(dayList, dayStart, dayEnd, options.id, options, fileData);
|
||
|
} else {
|
||
|
for (var id in history) {
|
||
|
getOneFileData(dayList, dayStart, dayEnd, id, options, fileData, true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
callback(fileData);
|
||
|
}
|
||
|
|
||
|
function sortByTs(a, b) {
|
||
|
var aTs = a.ts;
|
||
|
var bTs = b.ts;
|
||
|
return ((aTs < bTs) ? -1 : ((aTs > bTs) ? 1 : 0));
|
||
|
}
|
||
|
|
||
|
function getHistory(msg) {
|
||
|
var startTime = new Date().getTime();
|
||
|
var options = {
|
||
|
id: msg.message.id ? msg.message.id : null,
|
||
|
path: adapter.config.storeDir,
|
||
|
start: msg.message.options.start,
|
||
|
end: msg.message.options.end || ((new Date()).getTime() + 5000000),
|
||
|
step: parseInt(msg.message.options.step, 10) || null,
|
||
|
count: parseInt(msg.message.options.count, 10) || 500,
|
||
|
from: msg.message.options.from || false,
|
||
|
ack: msg.message.options.ack || false,
|
||
|
q: msg.message.options.q || false,
|
||
|
ignoreNull: msg.message.options.ignoreNull,
|
||
|
aggregate: msg.message.options.aggregate || 'average', // One of: max, min, average, total
|
||
|
limit: parseInt(msg.message.options.limit || adapter.config.limit || 2000, 10),
|
||
|
addId: msg.message.options.addId || false,
|
||
|
sessionId: msg.message.options.sessionId
|
||
|
};
|
||
|
if (options.id && aliasMap[options.id]) {
|
||
|
options.id = aliasMap[options.id];
|
||
|
}
|
||
|
if (options.start > options.end) {
|
||
|
var _end = options.end;
|
||
|
options.end = options.start;
|
||
|
options.start = _end;
|
||
|
}
|
||
|
|
||
|
// if less 2000.01.01 00:00:00
|
||
|
if (options.start < 946681200000) {
|
||
|
options.start *= 1000;
|
||
|
if (options.step !== null && options.step !== undefined) options.step *= 1000;
|
||
|
}
|
||
|
|
||
|
// if less 2000.01.01 00:00:00
|
||
|
if (options.end < 946681200000) options.end *= 1000;
|
||
|
|
||
|
if ((!options.start && options.count) || options.aggregate === 'onchange' || options.aggregate === '' || options.aggregate === 'none') {
|
||
|
getCachedData(options, function (cacheData, isFull) {
|
||
|
adapter.log.debug('after getCachedData: length = ' + cacheData.length + ', isFull=' + isFull);
|
||
|
// if all data read
|
||
|
if (isFull && cacheData.length) {
|
||
|
cacheData = cacheData.sort(sortByTs);
|
||
|
if ((options.count) && (cacheData.length > options.count) && (options.aggregate === 'none')) {
|
||
|
cacheData = cacheData.slice(0, options.count);
|
||
|
adapter.log.debug('cut cacheData to ' + options.count + ' values');
|
||
|
}
|
||
|
adapter.log.debug('Send: ' + cacheData.length + ' values in: ' + (new Date().getTime() - startTime) + 'ms');
|
||
|
adapter.sendTo(msg.from, msg.command, {
|
||
|
result: cacheData,
|
||
|
step: null,
|
||
|
error: null
|
||
|
}, msg.callback);
|
||
|
} else {
|
||
|
var origCount = options.count;
|
||
|
options.count -= cacheData.length;
|
||
|
getFileData(options, function (fileData) {
|
||
|
adapter.log.debug('after getFileData: cacheData.length = ' + cacheData.length + ', fileData.length = ' + fileData.length);
|
||
|
cacheData = cacheData.concat(fileData);
|
||
|
cacheData = cacheData.sort(sortByTs);
|
||
|
options.result = cacheData;
|
||
|
options.count = origCount;
|
||
|
Aggregate.beautify(options);
|
||
|
|
||
|
adapter.log.debug('Send: ' + options.result.length + ' values in: ' + (new Date().getTime() - startTime) + 'ms');
|
||
|
adapter.sendTo(msg.from, msg.command, {
|
||
|
result: options.result,
|
||
|
step: null,
|
||
|
error: null
|
||
|
}, msg.callback);
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
} else {
|
||
|
// to use parallel requests activate this.
|
||
|
if (1 || typeof GetHistory === 'undefined') {
|
||
|
adapter.log.debug('use parallel requests');
|
||
|
var gh = cp.fork(__dirname + '/lib/getHistory.js', [JSON.stringify(options)], {silent: false});
|
||
|
|
||
|
var ghTimeout = setTimeout(function () {
|
||
|
try {
|
||
|
gh.kill('SIGINT');
|
||
|
}
|
||
|
catch (err) {
|
||
|
adapter.log.error(err);
|
||
|
}
|
||
|
}, 120000);
|
||
|
|
||
|
gh.on('message', function (data) {
|
||
|
var cmd = data[0];
|
||
|
if (cmd === 'getCache') {
|
||
|
var settings = data[1];
|
||
|
getCachedData(settings, function (cacheData) {
|
||
|
gh.send(['cacheData', cacheData]);
|
||
|
});
|
||
|
} else if (cmd === 'response') {
|
||
|
clearTimeout(ghTimeout);
|
||
|
ghTimeout = null;
|
||
|
|
||
|
var result = data[1];
|
||
|
var overallLength = data[2];
|
||
|
var step = data[3];
|
||
|
if (result) {
|
||
|
adapter.log.debug('Send: ' + result.length + ' of: ' + overallLength + ' in: ' + (new Date().getTime() - startTime) + 'ms');
|
||
|
adapter.sendTo(msg.from, msg.command, {
|
||
|
result: result,
|
||
|
step: step,
|
||
|
error: null
|
||
|
}, msg.callback);
|
||
|
} else {
|
||
|
adapter.log.info('No Data');
|
||
|
adapter.sendTo(msg.from, msg.command, {
|
||
|
result: [],
|
||
|
step: null,
|
||
|
error: null
|
||
|
}, msg.callback);
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
} else {
|
||
|
GetHistory.initAggregate(options);
|
||
|
GetHistory.getFileData(options);
|
||
|
getCachedData(options, function (cachedData) {
|
||
|
GetHistory.aggregation(options, cachedData);
|
||
|
var data = GetHistory.response(options);
|
||
|
|
||
|
if (data[0] === 'response') {
|
||
|
if (data[1]) {
|
||
|
adapter.log.debug('Send: ' + data[1].length + ' of: ' + data[2] + ' in: ' + (new Date().getTime() - startTime) + 'ms');
|
||
|
adapter.sendTo(msg.from, msg.command, {
|
||
|
result: data[1],
|
||
|
step: data[3],
|
||
|
error: null
|
||
|
}, msg.callback);
|
||
|
} else {
|
||
|
adapter.log.info('No Data');
|
||
|
adapter.sendTo(msg.from, msg.command, {
|
||
|
result: [],
|
||
|
step: null,
|
||
|
error: null
|
||
|
}, msg.callback);
|
||
|
}
|
||
|
} else {
|
||
|
adapter.log.error('Unknown response type: ' + data[0]);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function getDirectories(path) {
|
||
|
return fs.readdirSync(path).filter(function (file) {
|
||
|
return fs.statSync(path + '/' + file).isDirectory();
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function storeState(msg) {
|
||
|
if (!msg.message || !msg.message.id || !msg.message.state) {
|
||
|
adapter.log.error('storeState called with invalid data');
|
||
|
adapter.sendTo(msg.from, msg.command, {
|
||
|
error: 'Invalid call'
|
||
|
}, msg.callback);
|
||
|
return;
|
||
|
}
|
||
|
var id;
|
||
|
if (Array.isArray(msg.message)) {
|
||
|
adapter.log.debug('storeState: store ' + msg.message.length + ' states for multiple ids');
|
||
|
for (var i = 0; i < msg.message.length; i++) {
|
||
|
id = aliasMap[msg.message[i].id] ? aliasMap[msg.message[i].id] : msg.message[i].id;
|
||
|
if (history[id]) {
|
||
|
history[id].state = msg.message[i].state;
|
||
|
pushHelper(id);
|
||
|
}
|
||
|
else {
|
||
|
adapter.log.warn('storeState: history not enabled for ' + msg.message[i].id + '. Ignoring');
|
||
|
}
|
||
|
}
|
||
|
} else if (Array.isArray(msg.message.state)) {
|
||
|
adapter.log.debug('storeState: store ' + msg.message.state.length + ' states for ' + msg.message.id);
|
||
|
for (var j = 0; j < msg.message.state.length; j++) {
|
||
|
id = aliasMap[msg.message.id] ? aliasMap[msg.message.id] : msg.message.id;
|
||
|
if (history[id]) {
|
||
|
history[id].state = msg.message.state[j];
|
||
|
pushHelper(id);
|
||
|
}
|
||
|
else {
|
||
|
adapter.log.warn('storeState: history not enabled for ' + msg.message.id + '. Ignoring');
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
adapter.log.debug('storeState: store 1 state for ' + msg.message.id);
|
||
|
id = aliasMap[msg.message.id] ? aliasMap[msg.message.id] : msg.message.id;
|
||
|
if (history[id]) {
|
||
|
history[id].state = msg.message.state;
|
||
|
pushHelper(id);
|
||
|
}
|
||
|
else {
|
||
|
adapter.log.warn('storeState: history not enabled for ' + msg.message.id + '. Ignoring');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
adapter.sendTo(msg.from, msg.command, {
|
||
|
success: true
|
||
|
}, msg.callback);
|
||
|
}
|
||
|
|
||
|
function enableHistory(msg) {
|
||
|
if (!msg.message || !msg.message.id) {
|
||
|
adapter.log.error('enableHistory called with invalid data');
|
||
|
adapter.sendTo(msg.from, msg.command, {
|
||
|
error: 'Invalid call'
|
||
|
}, msg.callback);
|
||
|
return;
|
||
|
}
|
||
|
var obj = {};
|
||
|
obj.common = {};
|
||
|
obj.common.custom = {};
|
||
|
if (msg.message.options) {
|
||
|
obj.common.custom[adapter.namespace] = msg.message.options;
|
||
|
}
|
||
|
else {
|
||
|
obj.common.custom[adapter.namespace] = {};
|
||
|
}
|
||
|
obj.common.custom[adapter.namespace].enabled = true;
|
||
|
adapter.extendForeignObject(msg.message.id, obj, function (err) {
|
||
|
if (err) {
|
||
|
adapter.log.error('enableHistory: ' + err);
|
||
|
adapter.sendTo(msg.from, msg.command, {
|
||
|
error: err
|
||
|
}, msg.callback);
|
||
|
} else {
|
||
|
adapter.log.info(JSON.stringify(obj));
|
||
|
adapter.sendTo(msg.from, msg.command, {
|
||
|
success: true
|
||
|
}, msg.callback);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function disableHistory(msg) {
|
||
|
if (!msg.message || !msg.message.id) {
|
||
|
adapter.log.error('disableHistory called with invalid data');
|
||
|
adapter.sendTo(msg.from, msg.command, {
|
||
|
error: 'Invalid call'
|
||
|
}, msg.callback);
|
||
|
return;
|
||
|
}
|
||
|
var obj = {};
|
||
|
obj.common = {};
|
||
|
obj.common.custom = {};
|
||
|
obj.common.custom[adapter.namespace] = {};
|
||
|
obj.common.custom[adapter.namespace].enabled = false;
|
||
|
adapter.extendForeignObject(msg.message.id, obj, function (err) {
|
||
|
if (err) {
|
||
|
adapter.log.error('disableHistory: ' + err);
|
||
|
adapter.sendTo(msg.from, msg.command, {
|
||
|
error: err
|
||
|
}, msg.callback);
|
||
|
} else {
|
||
|
adapter.log.info(JSON.stringify(obj));
|
||
|
adapter.sendTo(msg.from, msg.command, {
|
||
|
success: true
|
||
|
}, msg.callback);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function getEnabledDPs(msg) {
|
||
|
var data = {};
|
||
|
for (var id in history) {
|
||
|
if (!history.hasOwnProperty(id)) continue;
|
||
|
data[history[id].realId] = history[id][adapter.namespace];
|
||
|
}
|
||
|
|
||
|
adapter.sendTo(msg.from, msg.command, data, msg.callback);
|
||
|
}
|