2365 lines
105 KiB
JavaScript
2365 lines
105 KiB
JavaScript
|
'use strict';
|
|||
|
// let context = {
|
|||
|
// adapter,
|
|||
|
// mods,
|
|||
|
// errorLogFunction,
|
|||
|
// subscriptions,
|
|||
|
// subscribedPatterns,
|
|||
|
// states,
|
|||
|
// adapterSubs,
|
|||
|
// objects,
|
|||
|
// cacheObjectEnums,
|
|||
|
// stateIds,
|
|||
|
// logWithLineInfo,
|
|||
|
// timers,
|
|||
|
// enums,
|
|||
|
// channels,
|
|||
|
// devices,
|
|||
|
// isEnums
|
|||
|
// };
|
|||
|
|
|||
|
/**
|
|||
|
* @typedef {Object} SandboxContext
|
|||
|
* @property {Record<string, string[]>} channels
|
|||
|
* @property {Record<string, string[]>} devices
|
|||
|
* @property {string[]} stateIds
|
|||
|
*/
|
|||
|
|
|||
|
/**
|
|||
|
* @param {{[prop: string]: any} & SandboxContext} context
|
|||
|
*/
|
|||
|
function sandBox(script, name, verbose, debug, context) {
|
|||
|
const consts = require('./consts');
|
|||
|
const words = require('./words');
|
|||
|
const eventObj = require('./eventObj');
|
|||
|
const patternCompareFunctions = require('./patternCompareFunctions');
|
|||
|
const nodeSchedule = require('node-schedule');
|
|||
|
|
|||
|
const adapter = context.adapter;
|
|||
|
const mods = context.mods;
|
|||
|
const states = context.states;
|
|||
|
const objects = context.objects;
|
|||
|
const timers = context.timers;
|
|||
|
const enums = context.enums;
|
|||
|
|
|||
|
function errorInCallback(e) {
|
|||
|
context.logError('Error in callback', e);
|
|||
|
}
|
|||
|
|
|||
|
function unsubscribePattern(script, pattern) {
|
|||
|
if (adapter.config.subscribe) {
|
|||
|
if (script.subscribes[pattern]) {
|
|||
|
script.subscribes[pattern]--;
|
|||
|
if (!script.subscribes[pattern]) delete script.subscribes[pattern];
|
|||
|
}
|
|||
|
|
|||
|
if (context.subscribedPatterns[pattern]) {
|
|||
|
context.subscribedPatterns[pattern]--;
|
|||
|
if (!context.subscribedPatterns[pattern]) {
|
|||
|
adapter.unsubscribeForeignStates(pattern);
|
|||
|
delete context.subscribedPatterns[pattern];
|
|||
|
|
|||
|
// if pattern was regex or with * some states will stay in RAM, but it is OK.
|
|||
|
if (states[pattern]) delete states[pattern];
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function subscribePattern(script, pattern) {
|
|||
|
if (adapter.config.subscribe) {
|
|||
|
if (!script.subscribes[pattern]) {
|
|||
|
script.subscribes[pattern] = 1;
|
|||
|
} else {
|
|||
|
script.subscribes[pattern]++;
|
|||
|
}
|
|||
|
|
|||
|
if (!context.subscribedPatterns[pattern]) {
|
|||
|
context.subscribedPatterns[pattern] = 1;
|
|||
|
adapter.subscribeForeignStates(pattern);
|
|||
|
|
|||
|
// request current value to deliver old value on change.
|
|||
|
if (typeof pattern === 'string' && pattern.indexOf('*') === -1) {
|
|||
|
adapter.getForeignState(pattern, function (err, state) {
|
|||
|
if (state) states[pattern] = state;
|
|||
|
});
|
|||
|
} else {
|
|||
|
adapter.getForeignStates(pattern, function (err, _states) {
|
|||
|
if (_states) {
|
|||
|
for (const id in _states) {
|
|||
|
if (!_states.hasOwnProperty(id)) continue;
|
|||
|
states[id] = _states[id];
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
} else {
|
|||
|
context.subscribedPatterns[pattern]++;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @typedef PatternCompareFunctionArray
|
|||
|
* @type {Array<any> & {logic?: string}}
|
|||
|
*/
|
|||
|
|
|||
|
function getPatternCompareFunctions(pattern) {
|
|||
|
let func;
|
|||
|
/** @type {PatternCompareFunctionArray} */
|
|||
|
const functions = [];
|
|||
|
functions.logic = pattern.logic || 'and';
|
|||
|
//adapter.log.info('## '+JSON.stringify(pattern));
|
|||
|
for (const key in pattern) {
|
|||
|
if (!pattern.hasOwnProperty(key)) continue;
|
|||
|
if (key === 'logic') continue;
|
|||
|
if (key === 'change' && pattern.change === 'any') continue;
|
|||
|
if (!(func = patternCompareFunctions[key])) continue;
|
|||
|
if (typeof (func = func(pattern)) !== 'function') continue;
|
|||
|
functions.push(func);
|
|||
|
}
|
|||
|
return functions;
|
|||
|
}
|
|||
|
|
|||
|
/** @typedef {{attr: string, value: string, idRegExp?: RegExp}} Selector */
|
|||
|
/**
|
|||
|
* Splits a selector string into attribute and value
|
|||
|
* @param {string} selector The selector string to split
|
|||
|
* @returns {Selector}
|
|||
|
*/
|
|||
|
function splitSelectorString(selector) {
|
|||
|
const parts = selector.split('=', 2);
|
|||
|
if (parts[1] && parts[1][0] === '"') {
|
|||
|
parts[1] = parts[1].substring(1);
|
|||
|
const len = parts[1].length;
|
|||
|
if (parts[1] && parts[1][len - 1] === '"') parts[1] = parts[1].substring(0, len - 1);
|
|||
|
}
|
|||
|
if (parts[1] && parts[1][0] === "'") {
|
|||
|
parts[1] = parts[1].substring(1);
|
|||
|
const len = parts[1].length;
|
|||
|
if (parts[1] && parts[1][len - 1] === "'") parts[1] = parts[1].substring(0, len - 1);
|
|||
|
}
|
|||
|
|
|||
|
if (parts[1]) parts[1] = parts[1].trim();
|
|||
|
parts[0] = parts[0].trim();
|
|||
|
|
|||
|
return {attr: parts[0], value: parts[1]};
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Transforms a selector string with wildcards into a regular expression
|
|||
|
* @param {string} str The selector string to transform into a regular expression
|
|||
|
*/
|
|||
|
function selectorStringToRegExp(str) {
|
|||
|
if (str[0] === '*') {
|
|||
|
// wildcard at the start, match the end of the string
|
|||
|
return new RegExp(str.replace(/\./g, '\\.').replace(/\*/g, '.*') + '$');
|
|||
|
} else if (str[str.length - 1] === '*') {
|
|||
|
// wildcard at the end, match the start of the string
|
|||
|
return new RegExp('^' + str.replace(/\./g, '\\.').replace(/\*/g, '.*'));
|
|||
|
} else {
|
|||
|
// wildcard potentially in the middle, match whatever
|
|||
|
return new RegExp(str.replace(/\./g, '\\.').replace(/\*/g, '.*'));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Adds a regular expression for selectors targeting the state ID
|
|||
|
* @param {Selector} selector The selector to apply the transform to
|
|||
|
* @returns {Selector}
|
|||
|
*/
|
|||
|
function addRegExpToIdAttrSelectors(selector) {
|
|||
|
if (
|
|||
|
(selector.attr === 'id' || selector.attr === 'state.id')
|
|||
|
&& (!selector.idRegExp && selector.value)
|
|||
|
) {
|
|||
|
return {
|
|||
|
attr: selector.attr,
|
|||
|
value: selector.value,
|
|||
|
idRegExp: selectorStringToRegExp(selector.value)
|
|||
|
};
|
|||
|
} else {
|
|||
|
return selector;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Tests if a value loosely equals (==) the reference string.
|
|||
|
* In contrast to the equality operator, this treats true == "true" aswell
|
|||
|
* so we can test common and native attributes for boolean values
|
|||
|
* @param {boolean | string | number | undefined} value The value to compare with the reference
|
|||
|
* @param {string} reference The reference to compare the value to
|
|||
|
*/
|
|||
|
function looselyEqualsString(value, reference) {
|
|||
|
// For booleans, compare the string representation
|
|||
|
// For other types do a loose comparison
|
|||
|
return (typeof value === 'boolean')
|
|||
|
? (value && reference === 'true') || (!value && reference === 'false')
|
|||
|
: value == reference
|
|||
|
;
|
|||
|
}
|
|||
|
|
|||
|
const sandbox = {
|
|||
|
mods: mods,
|
|||
|
_id: script._id,
|
|||
|
name: name, // deprecated
|
|||
|
scriptName: name,
|
|||
|
instance: adapter.instance,
|
|||
|
verbose: verbose,
|
|||
|
request: mods.request,
|
|||
|
exports: {}, // Polyfill for the exports object in TypeScript modules
|
|||
|
require: function (md) {
|
|||
|
console.log('REQUIRE: ' + md);
|
|||
|
if (mods[md]) {
|
|||
|
return mods[md];
|
|||
|
} else {
|
|||
|
try {
|
|||
|
mods[md] = require(__dirname + '/../node_modules/' + md);
|
|||
|
return mods[md];
|
|||
|
} catch (e) {
|
|||
|
try {
|
|||
|
mods[md] = require(__dirname + '/../../' + md);
|
|||
|
return mods[md];
|
|||
|
} catch (e) {
|
|||
|
context.logError(name, e, 6);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
},
|
|||
|
Buffer: Buffer,
|
|||
|
__engine: {
|
|||
|
__subscriptions: 0,
|
|||
|
__schedules: 0
|
|||
|
},
|
|||
|
/**
|
|||
|
* @param {string} selector
|
|||
|
* @returns {iobJS.QueryResult}
|
|||
|
*/
|
|||
|
$: function (selector) {
|
|||
|
// following is supported
|
|||
|
// 'type[commonAttr=something]', 'id[commonAttr=something]', id(enumName="something")', id{nativeName="something"}
|
|||
|
// Type can be state, channel or device
|
|||
|
// Attr can be any of the common attributes and can have wildcards *
|
|||
|
// E.g. "state[id='hm-rpc.0.*]" or "hm-rpc.0.*" returns all states of adapter instance hm-rpc.0
|
|||
|
// channel(room="Living room") => all states in room "Living room"
|
|||
|
// channel{TYPE=BLIND}[state.id=*.LEVEL]
|
|||
|
// Switch all states with .STATE of channels with role "switch" in "Wohnzimmer" to false
|
|||
|
// $('channel[role=switch][state.id=*.STATE](rooms=Wohnzimmer)').setState(false);
|
|||
|
//
|
|||
|
// Following functions are possible, setValue, getValue (only from first), on, each
|
|||
|
|
|||
|
// Todo CACHE!!!
|
|||
|
|
|||
|
const result = {};
|
|||
|
|
|||
|
let name = '';
|
|||
|
/** @type {string[]} */
|
|||
|
const commonStrings = [];
|
|||
|
/** @type {string[]} */
|
|||
|
const enumStrings = [];
|
|||
|
/** @type {string[]} */
|
|||
|
const nativeStrings = [];
|
|||
|
let isInsideName = true;
|
|||
|
let isInsideCommonString = false;
|
|||
|
let isInsideEnumString = false;
|
|||
|
let isInsideNativeString = false;
|
|||
|
let currentCommonString = '';
|
|||
|
let currentNativeString = '';
|
|||
|
let currentEnumString = '';
|
|||
|
|
|||
|
// parse string
|
|||
|
let selectorHasInvalidType = false;
|
|||
|
if (typeof selector === 'string') {
|
|||
|
for (let i = 0; i < selector.length; i++) {
|
|||
|
if (selector[i] === '{') {
|
|||
|
isInsideName = false;
|
|||
|
if (isInsideCommonString || isInsideEnumString || isInsideNativeString) {
|
|||
|
// Error
|
|||
|
break;
|
|||
|
}
|
|||
|
isInsideNativeString = true;
|
|||
|
} else if (selector[i] === '}') {
|
|||
|
isInsideNativeString = false;
|
|||
|
nativeStrings.push(currentNativeString);
|
|||
|
currentNativeString = '';
|
|||
|
} else if (selector[i] === '[') {
|
|||
|
isInsideName = false;
|
|||
|
if (isInsideCommonString || isInsideEnumString || isInsideNativeString) {
|
|||
|
// Error
|
|||
|
break;
|
|||
|
}
|
|||
|
isInsideCommonString = true;
|
|||
|
} else if (selector[i] === ']') {
|
|||
|
isInsideCommonString = false;
|
|||
|
commonStrings.push(currentCommonString);
|
|||
|
currentCommonString = '';
|
|||
|
} else if (selector[i] === '(') {
|
|||
|
isInsideName = false;
|
|||
|
if (isInsideCommonString || isInsideEnumString || isInsideNativeString) {
|
|||
|
// Error
|
|||
|
break;
|
|||
|
}
|
|||
|
isInsideEnumString = true;
|
|||
|
} else if (selector[i] === ')') {
|
|||
|
isInsideEnumString = false;
|
|||
|
enumStrings.push(currentEnumString);
|
|||
|
currentEnumString = '';
|
|||
|
} else if (isInsideName) {
|
|||
|
name += selector[i];
|
|||
|
} else if (isInsideCommonString) {
|
|||
|
currentCommonString += selector[i];
|
|||
|
} else if (isInsideEnumString) {
|
|||
|
currentEnumString += selector[i];
|
|||
|
} else if (isInsideNativeString) {
|
|||
|
currentNativeString += selector[i];
|
|||
|
} //else {
|
|||
|
// some error
|
|||
|
//}
|
|||
|
}
|
|||
|
} else {
|
|||
|
selectorHasInvalidType = true;
|
|||
|
}
|
|||
|
|
|||
|
// If some error in the selector
|
|||
|
if (selectorHasInvalidType || isInsideEnumString || isInsideCommonString || isInsideNativeString) {
|
|||
|
result.length = 0;
|
|||
|
result.each = function () {
|
|||
|
return this;
|
|||
|
};
|
|||
|
result.getState = function () {
|
|||
|
return null;
|
|||
|
};
|
|||
|
result.setState = function () {
|
|||
|
return this;
|
|||
|
};
|
|||
|
result.on = function () {
|
|||
|
return this;
|
|||
|
};
|
|||
|
}
|
|||
|
|
|||
|
if (isInsideEnumString) {
|
|||
|
adapter.log.warn('Invalid selector: enum close bracket ")" cannot be found in "' + selector + '"');
|
|||
|
result.error = 'Invalid selector: enum close bracket ")" cannot be found';
|
|||
|
return result;
|
|||
|
} else if (isInsideCommonString) {
|
|||
|
adapter.log.warn('Invalid selector: common close bracket "]" cannot be found in "' + selector + '"');
|
|||
|
result.error = 'Invalid selector: common close bracket "]" cannot be found';
|
|||
|
return result;
|
|||
|
} else if (isInsideNativeString) {
|
|||
|
adapter.log.warn('Invalid selector: native close bracket "}" cannot be found in "' + selector + '"');
|
|||
|
result.error = 'Invalid selector: native close bracket "}" cannot be found';
|
|||
|
return result;
|
|||
|
} else if (selectorHasInvalidType) {
|
|||
|
const message = `Invalid selector: selector must be a string but is of type ${typeof selector}`;
|
|||
|
adapter.log.warn(message);
|
|||
|
result.error = message;
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
/** @type {Selector[]} */
|
|||
|
let commonSelectors = commonStrings.map(selector => splitSelectorString(selector));
|
|||
|
let nativeSelectors = nativeStrings.map(selector => splitSelectorString(selector));
|
|||
|
const enumSelectorObjects = enumStrings.map(_enum => splitSelectorString(_enum));
|
|||
|
const allSelectors = commonSelectors.concat(nativeSelectors, enumSelectorObjects);
|
|||
|
|
|||
|
// These selectors match the state or object ID and don't belong in the common/native selectors
|
|||
|
// Also use RegExp for the ID matching
|
|||
|
const stateIdSelectors = allSelectors
|
|||
|
.filter(selector => selector.attr === 'state.id')
|
|||
|
.map(selector => addRegExpToIdAttrSelectors(selector))
|
|||
|
;
|
|||
|
const objectIdSelectors = allSelectors
|
|||
|
.filter(selector => selector.attr === 'id')
|
|||
|
.map(selector => addRegExpToIdAttrSelectors(selector))
|
|||
|
;
|
|||
|
|
|||
|
commonSelectors = commonSelectors.filter(selector => selector.attr !== 'state.id' && selector.attr !== 'id');
|
|||
|
nativeSelectors = nativeSelectors.filter(selector => selector.attr !== 'state.id' && selector.attr !== 'id');
|
|||
|
const enumSelectors = enumSelectorObjects
|
|||
|
.filter(selector => selector.attr !== 'state.id' && selector.attr !== 'id')
|
|||
|
// enums are filtered by their enum id, so transform the selector into that
|
|||
|
.map(selector => `enum.${selector.attr}.${selector.value}`)
|
|||
|
;
|
|||
|
|
|||
|
name = name.trim();
|
|||
|
if (name === 'channel' || name === 'device') {
|
|||
|
// Fill the channels and devices objects with the IDs of all their states
|
|||
|
// so we can loop over them afterwards
|
|||
|
if (!context.channels || !context.devices) {
|
|||
|
context.channels = {};
|
|||
|
context.devices = {};
|
|||
|
for (const _id in objects) {
|
|||
|
if (objects.hasOwnProperty(_id) && objects[_id].type === 'state') {
|
|||
|
const parts = _id.split('.');
|
|||
|
parts.pop();
|
|||
|
const chn = parts.join('.');
|
|||
|
|
|||
|
parts.pop();
|
|||
|
const dev = parts.join('.');
|
|||
|
|
|||
|
context.devices[dev] = context.devices[dev] || [];
|
|||
|
context.devices[dev].push(_id);
|
|||
|
|
|||
|
context.channels[chn] = context.channels[chn] || [];
|
|||
|
context.channels[chn].push(_id);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* applies all selectors targeting an object or state ID
|
|||
|
* @param {string} objId
|
|||
|
* @param {Selector[]} selectors
|
|||
|
*/
|
|||
|
function applyIDSelectors(objId, selectors) {
|
|||
|
// Only keep the ID if it matches every ID selector
|
|||
|
return selectors.every(selector => {
|
|||
|
return selector.idRegExp == null || selector.idRegExp.test(objId);
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Applies all selectors targeting the Object common properties
|
|||
|
* @param {string} objId - The ID of the object in question
|
|||
|
*/
|
|||
|
function applyCommonSelectors(objId) {
|
|||
|
const obj = objects[objId];
|
|||
|
if (obj == null || obj.common == null) return false;
|
|||
|
const objCommon = obj.common;
|
|||
|
// make sure this object satisfies all selectors
|
|||
|
return commonSelectors.every(selector => {
|
|||
|
return (
|
|||
|
// ensure a property exists
|
|||
|
(selector.value === undefined && objCommon[selector.attr] !== undefined)
|
|||
|
// or match exact values
|
|||
|
|| looselyEqualsString(objCommon[selector.attr], selector.value)
|
|||
|
);
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Applies all selectors targeting the Object native properties
|
|||
|
* @param {string} objId - The ID of the object in question
|
|||
|
*/
|
|||
|
function applyNativeSelectors(objId) {
|
|||
|
const obj = objects[objId];
|
|||
|
if (obj == null || obj.native == null) return false;
|
|||
|
const objNative = obj.native;
|
|||
|
// make sure this object satisfies all selectors
|
|||
|
return nativeSelectors.every(selector => {
|
|||
|
return (
|
|||
|
// ensure a property exists
|
|||
|
(selector.value === undefined && objNative[selector.attr] !== undefined)
|
|||
|
// or match exact values
|
|||
|
|| looselyEqualsString(objNative[selector.attr], selector.value)
|
|||
|
);
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Applies all selectors targeting the Objects enums
|
|||
|
* @param {string} objId - The ID of the object in question
|
|||
|
*/
|
|||
|
function applyEnumSelectors(objId) {
|
|||
|
const enumIds = [];
|
|||
|
eventObj.getObjectEnumsSync(context, objId, enumIds);
|
|||
|
// make sure this object satisfies all selectors
|
|||
|
return enumSelectors.every(_enum => enumIds.indexOf(_enum) > -1);
|
|||
|
}
|
|||
|
|
|||
|
/** @type {string[]} */
|
|||
|
let res = [];
|
|||
|
|
|||
|
if (name === 'channel') {
|
|||
|
// go through all channels
|
|||
|
res = Object.keys(context.channels)
|
|||
|
// filter out those that don't match every ID selector for the channel ID
|
|||
|
.filter(channelId => applyIDSelectors(channelId, objectIdSelectors))
|
|||
|
// filter out those that don't match every common selector
|
|||
|
.filter(channelId => applyCommonSelectors(channelId))
|
|||
|
// filter out those that don't match every native selector
|
|||
|
.filter(channelId => applyNativeSelectors(channelId))
|
|||
|
// filter out those that don't match every enum selector
|
|||
|
.filter(channelId => applyEnumSelectors(channelId))
|
|||
|
// retrieve the state ID collection for all remaining channels
|
|||
|
.map(id => context.channels[id])
|
|||
|
// and flatten the array to get only the state IDs
|
|||
|
.reduce((acc, next) => acc.concat(next), [])
|
|||
|
// now filter out those that don't match every ID selector for the state ID
|
|||
|
.filter(stateId => applyIDSelectors(stateId, stateIdSelectors))
|
|||
|
;
|
|||
|
|
|||
|
} else if (name === 'device') {
|
|||
|
// go through all devices
|
|||
|
res = Object.keys(context.devices)
|
|||
|
// filter out those that don't match every ID selector for the channel ID
|
|||
|
.filter(deviceId => applyIDSelectors(deviceId, objectIdSelectors))
|
|||
|
// filter out those that don't match every common selector
|
|||
|
.filter(deviceId => applyCommonSelectors(deviceId))
|
|||
|
// filter out those that don't match every native selector
|
|||
|
.filter(deviceId => applyNativeSelectors(deviceId))
|
|||
|
// filter out those that don't match every enum selector
|
|||
|
.filter(deviceId => applyEnumSelectors(deviceId))
|
|||
|
// retrieve the state ID collection for all remaining devices
|
|||
|
.map(id => context.devices[id])
|
|||
|
// and flatten the array to get only the state IDs
|
|||
|
.reduce((acc, next) => acc.concat(next), [])
|
|||
|
// now filter out those that don't match every ID selector for the state ID
|
|||
|
.filter(stateId => applyIDSelectors(stateId, stateIdSelectors))
|
|||
|
;
|
|||
|
|
|||
|
} else {
|
|||
|
// go through all states
|
|||
|
res = context.stateIds;
|
|||
|
// if the "name" is not state then we filter for the ID aswell
|
|||
|
if (name && name !== 'state') {
|
|||
|
const r = new RegExp('^' + name.replace(/\./g, '\\.').replace(/\*/g, '.*') + '$');
|
|||
|
res = res.filter(id => r.test(id));
|
|||
|
}
|
|||
|
|
|||
|
res = res
|
|||
|
// filter out those that don't match every ID selector for the object ID or the state ID
|
|||
|
.filter(id => applyIDSelectors(id, objectIdSelectors))
|
|||
|
.filter(id => applyIDSelectors(id, stateIdSelectors))
|
|||
|
// filter out those that don't match every common selector
|
|||
|
.filter(id => applyCommonSelectors(id))
|
|||
|
// filter out those that don't match every native selector
|
|||
|
.filter(id => applyNativeSelectors(id))
|
|||
|
// filter out those that don't match every enum selector
|
|||
|
.filter(id => applyEnumSelectors(id))
|
|||
|
;
|
|||
|
}
|
|||
|
|
|||
|
for (let i = 0; i < res.length; i++) {
|
|||
|
result[i] = res[i];
|
|||
|
}
|
|||
|
result.length = res.length;
|
|||
|
result.each = function (callback) {
|
|||
|
if (typeof callback === 'function') {
|
|||
|
let r;
|
|||
|
for (let i = 0; i < this.length; i++) {
|
|||
|
r = callback(result[i], i);
|
|||
|
if (r === false) break;
|
|||
|
}
|
|||
|
}
|
|||
|
return this;
|
|||
|
};
|
|||
|
result.getState = function (callback) {
|
|||
|
if (adapter.config.subscribe) {
|
|||
|
if (typeof callback !== 'function') {
|
|||
|
sandbox.log('You cannot use this function synchronous', 'error');
|
|||
|
} else {
|
|||
|
adapter.getForeignState(this[0], callback);
|
|||
|
}
|
|||
|
} else {
|
|||
|
if (this[0]) return states[this[0]];
|
|||
|
return null;
|
|||
|
}
|
|||
|
};
|
|||
|
result.setState = function (state, isAck, callback) {
|
|||
|
if (typeof isAck === 'function') {
|
|||
|
callback = isAck;
|
|||
|
isAck = undefined;
|
|||
|
}
|
|||
|
|
|||
|
if (isAck === true || isAck === false || isAck === 'true' || isAck === 'false') {
|
|||
|
if (typeof state === 'object') {
|
|||
|
state.ack = isAck;
|
|||
|
} else {
|
|||
|
state = {val: state, ack: isAck};
|
|||
|
}
|
|||
|
}
|
|||
|
let cnt = 0;
|
|||
|
for (let i = 0; i < this.length; i++) {
|
|||
|
cnt++;
|
|||
|
adapter.setForeignState(this[i], state, function () {
|
|||
|
if (!--cnt && typeof callback === 'function') callback();
|
|||
|
});
|
|||
|
}
|
|||
|
return this;
|
|||
|
};
|
|||
|
result.on = function (callbackOrId, value) {
|
|||
|
for (let i = 0; i < this.length; i++) {
|
|||
|
sandbox.subscribe(this[i], callbackOrId, value);
|
|||
|
}
|
|||
|
return this;
|
|||
|
};
|
|||
|
return result;
|
|||
|
},
|
|||
|
log: function (msg, sev) {
|
|||
|
if (!sev) sev = 'info';
|
|||
|
if (!adapter.log[sev]) {
|
|||
|
msg = 'Unknown severity level "' + sev + '" by log of [' + msg + ']';
|
|||
|
sev = 'warn';
|
|||
|
}
|
|||
|
adapter.log[sev](name + ': ' + msg);
|
|||
|
},
|
|||
|
exec: function (cmd, callback) {
|
|||
|
if (!adapter.config.enableExec) {
|
|||
|
const error = 'exec is not available. Please enable "Enable Exec" option in instance settings';
|
|||
|
adapter.log.error(error);
|
|||
|
sandbox.log(error);
|
|||
|
if (typeof callback === 'function') {
|
|||
|
setImmediate(callback, error);
|
|||
|
}
|
|||
|
} else {
|
|||
|
if (sandbox.verbose) {
|
|||
|
sandbox.log('exec: ' + cmd, 'info');
|
|||
|
}
|
|||
|
if (debug) {
|
|||
|
sandbox.log(words._('Command %s was not executed, while debug mode is active', cmd), 'warn');
|
|||
|
if (typeof callback === 'function') {
|
|||
|
setImmediate(function () {
|
|||
|
callback();
|
|||
|
});
|
|||
|
}
|
|||
|
} else {
|
|||
|
return mods.child_process.exec(cmd, callback);
|
|||
|
}
|
|||
|
}
|
|||
|
},
|
|||
|
email: function (msg) {
|
|||
|
if (sandbox.verbose) sandbox.log('email(msg=' + JSON.stringify(msg) + ')', 'info');
|
|||
|
adapter.sendTo('email', msg);
|
|||
|
},
|
|||
|
pushover: function (msg) {
|
|||
|
if (sandbox.verbose) sandbox.log('pushover(msg=' + JSON.stringify(msg) + ')', 'info');
|
|||
|
adapter.sendTo('pushover', msg);
|
|||
|
},
|
|||
|
subscribe: function (pattern, callbackOrId, value) {
|
|||
|
if (pattern && Array.isArray(pattern)) {
|
|||
|
const result = [];
|
|||
|
for (let t = 0; t < pattern.length; t++) {
|
|||
|
result.push(sandbox.subscribe(pattern[t], callbackOrId, value));
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
if (pattern && pattern.id && Array.isArray(pattern.id)) {
|
|||
|
const result_ = [];
|
|||
|
for (let tt = 0; tt < pattern.id.length; tt++) {
|
|||
|
const pa = JSON.parse(JSON.stringify(pattern));
|
|||
|
pa.id = pattern.id[tt];
|
|||
|
result_.push(sandbox.subscribe(pa, callbackOrId, value));
|
|||
|
}
|
|||
|
return result_;
|
|||
|
}
|
|||
|
|
|||
|
// try to detect astro or cron (by spaces)
|
|||
|
if (typeof pattern === 'object' || (typeof pattern === 'string' && pattern.match(/[,/\d*]+\s[,/\d*]+\s[,/\d*]+/))) {
|
|||
|
if (pattern.astro) {
|
|||
|
return sandbox.schedule(pattern, callbackOrId);
|
|||
|
} else if (pattern.time) {
|
|||
|
return sandbox.schedule(pattern.time, callbackOrId);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
let callback;
|
|||
|
|
|||
|
sandbox.__engine.__subscriptions += 1;
|
|||
|
|
|||
|
// source is set by regexp if defined as /regexp/
|
|||
|
if (typeof pattern !== 'object' || pattern instanceof RegExp || pattern.source) {
|
|||
|
pattern = {id: pattern, change: 'ne'};
|
|||
|
}
|
|||
|
|
|||
|
if (pattern.id !== undefined && !pattern.id) {
|
|||
|
adapter.log.error('Error by subscription: empty ID defined. All states matched.');
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// add adapter namespace if nothing given
|
|||
|
if (pattern.id && typeof pattern.id === 'string' && pattern.id.indexOf('.') === -1) {
|
|||
|
pattern.id = adapter.namespace + '.' + pattern.id;
|
|||
|
}
|
|||
|
|
|||
|
if (typeof callbackOrId === 'function') {
|
|||
|
callback = callbackOrId;
|
|||
|
} else {
|
|||
|
const that = this;
|
|||
|
if (typeof value === 'undefined') {
|
|||
|
callback = function (obj) {
|
|||
|
that.setState(callbackOrId, obj.newState.val);
|
|||
|
};
|
|||
|
} else {
|
|||
|
callback = function (/* obj */) {
|
|||
|
that.setState(callbackOrId, value);
|
|||
|
};
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const subs = {
|
|||
|
pattern: pattern,
|
|||
|
callback: function (obj) {
|
|||
|
if (typeof callback === 'function') {
|
|||
|
try {
|
|||
|
callback.call(sandbox, obj);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); // adapter.log.error('Error in callback: ' + e);
|
|||
|
}
|
|||
|
}
|
|||
|
},
|
|||
|
name: name
|
|||
|
};
|
|||
|
|
|||
|
// try to extract adapter
|
|||
|
if (pattern.id && typeof pattern.id === 'string') {
|
|||
|
const parts = pattern.id.split('.');
|
|||
|
const a = parts[0] + '.' + parts[1];
|
|||
|
const _adapter = 'system.adapter.' + a;
|
|||
|
|
|||
|
if (objects[_adapter] && objects[_adapter].common && objects[_adapter].common.subscribable) {
|
|||
|
const alive = 'system.adapter.' + a + '.alive';
|
|||
|
context.adapterSubs[alive] = context.adapterSubs[alive] || [];
|
|||
|
|
|||
|
const subExists = context.adapterSubs[alive].filter(function (sub) {
|
|||
|
return sub === pattern.id;
|
|||
|
}).length > 0;
|
|||
|
|
|||
|
if (!subExists) {
|
|||
|
context.adapterSubs[alive].push(pattern.id);
|
|||
|
adapter.sendTo(a, 'subscribe', pattern.id);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
if (sandbox.verbose) sandbox.log('subscribe: ' + JSON.stringify(subs), 'info');
|
|||
|
|
|||
|
subscribePattern(script, pattern.id);
|
|||
|
|
|||
|
subs.patternCompareFunctions = getPatternCompareFunctions(pattern);
|
|||
|
context.subscriptions.push(subs);
|
|||
|
|
|||
|
if (pattern.enumName || pattern.enumId) context.isEnums = true;
|
|||
|
return subs;
|
|||
|
},
|
|||
|
getSubscriptions: function () {
|
|||
|
const result = {};
|
|||
|
for (let s = 0; s < context.subscriptions.length; s++) {
|
|||
|
result[context.subscriptions[s].pattern.id] = result[context.subscriptions[s].pattern.id] || [];
|
|||
|
result[context.subscriptions[s].pattern.id].push({ name: context.subscriptions[s].name, pattern: context.subscriptions[s].pattern });
|
|||
|
}
|
|||
|
if (sandbox.verbose) sandbox.log('getSubscriptions() => ' + JSON.stringify(result), 'info');
|
|||
|
return result;
|
|||
|
},
|
|||
|
adapterSubscribe: function (id) {
|
|||
|
if (typeof id !== 'string') {
|
|||
|
adapter.log.error('adapterSubscribe: invalid type of id' + typeof id);
|
|||
|
return;
|
|||
|
}
|
|||
|
const parts = id.split('.');
|
|||
|
const _adapter = 'system.adapter.' + parts[0] + '.' + parts[1];
|
|||
|
if (objects[_adapter] && objects[_adapter].common && objects[_adapter].common.subscribable) {
|
|||
|
const a = parts[0] + '.' + parts[1];
|
|||
|
const alive = 'system.adapter.' + a + '.alive';
|
|||
|
context.adapterSubs[alive] = context.adapterSubs[alive] || [];
|
|||
|
context.adapterSubs[alive].push(id);
|
|||
|
if (sandbox.verbose) sandbox.log('adapterSubscribe: ' + a + ' - ' + id, 'info');
|
|||
|
adapter.sendTo(a, 'subscribe', id);
|
|||
|
}
|
|||
|
},
|
|||
|
adapterUnsubscribe: function (id) {
|
|||
|
return sandbox.unsubscribe(id);
|
|||
|
},
|
|||
|
unsubscribe: function (idOrObject) {
|
|||
|
if (idOrObject && Array.isArray(idOrObject)) {
|
|||
|
const result = [];
|
|||
|
for (let t = 0; t < idOrObject.length; t++) {
|
|||
|
result.push(sandbox.unsubscribe(idOrObject[t]));
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
if (sandbox.verbose) sandbox.log('adapterUnsubscribe(id=' + idOrObject + ')', 'info');
|
|||
|
if (typeof idOrObject === 'object') {
|
|||
|
for (let i = context.subscriptions.length - 1; i >= 0; i--) {
|
|||
|
if (context.subscriptions[i] === idOrObject) {
|
|||
|
unsubscribePattern(context.subscriptions[i].pattern.id);
|
|||
|
context.subscriptions.splice(i, 1);
|
|||
|
sandbox.__engine.__subscriptions--;
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
let deleted = 0;
|
|||
|
for (let i = context.subscriptions.length - 1; i >= 0; i--) {
|
|||
|
if (context.subscriptions[i].name === name && context.subscriptions[i].pattern.id === idOrObject) {
|
|||
|
deleted++;
|
|||
|
unsubscribePattern(context.subscriptions[i].pattern.id);
|
|||
|
context.subscriptions.splice(i, 1);
|
|||
|
sandbox.__engine.__subscriptions--;
|
|||
|
}
|
|||
|
}
|
|||
|
return !!deleted;
|
|||
|
}
|
|||
|
},
|
|||
|
on: function (pattern, callbackOrId, value) {
|
|||
|
return sandbox.subscribe(pattern, callbackOrId, value);
|
|||
|
},
|
|||
|
schedule: function (pattern, callback) {
|
|||
|
if (typeof callback !== 'function') {
|
|||
|
adapter.log.error(name + ': schedule callback missing');
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
sandbox.__engine.__schedules += 1;
|
|||
|
|
|||
|
if (pattern.astro) {
|
|||
|
|
|||
|
const nowdate = new Date();
|
|||
|
|
|||
|
if (adapter.config.latitude === undefined || adapter.config.longitude === undefined ||
|
|||
|
adapter.config.latitude === '' || adapter.config.longitude === '' ||
|
|||
|
adapter.config.latitude === null || adapter.config.longitude === null) {
|
|||
|
adapter.log.error('Longitude or latitude does not set. Cannot use astro.');
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
let ts = mods.suncalc.getTimes(nowdate, adapter.config.latitude, adapter.config.longitude)[pattern.astro];
|
|||
|
|
|||
|
if (ts.getTime().toString() === 'NaN') {
|
|||
|
adapter.log.warn('Cannot calculate "' + pattern.astro + '" for ' + adapter.config.latitude + ', ' + adapter.config.longitude);
|
|||
|
ts = new Date(nowdate.getTime());
|
|||
|
|
|||
|
if (pattern.astro === 'sunriseEnd' ||
|
|||
|
pattern.astro === 'goldenHourEnd' ||
|
|||
|
pattern.astro === 'sunset' ||
|
|||
|
pattern.astro === 'nightEnd' ||
|
|||
|
pattern.astro === 'nauticalDusk') {
|
|||
|
ts.setMinutes(59);
|
|||
|
ts.setHours(23);
|
|||
|
ts.setSeconds(59);
|
|||
|
} else {
|
|||
|
ts.setMinutes(59);
|
|||
|
ts.setHours(23);
|
|||
|
ts.setSeconds(58);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (ts && pattern.shift) {
|
|||
|
ts = new Date(ts.getTime() + (pattern.shift * 60000));
|
|||
|
}
|
|||
|
|
|||
|
if (!ts || ts < nowdate) {
|
|||
|
const date = new Date(nowdate);
|
|||
|
// Event doesn't occur today - try again tomorrow
|
|||
|
// Calculate time till 24:00 and set timeout
|
|||
|
date.setDate(date.getDate() + 1);
|
|||
|
date.setMinutes(1); // Somtimes timer fires at 23:59:59
|
|||
|
date.setHours(0);
|
|||
|
date.setSeconds(0);
|
|||
|
date.setMilliseconds(0);
|
|||
|
date.setMinutes(-date.getTimezoneOffset());
|
|||
|
|
|||
|
|
|||
|
// Calculate new schedule in the next day
|
|||
|
sandbox.setTimeout(function () {
|
|||
|
if (sandbox.__engine.__schedules > 0) sandbox.__engine.__schedules--;
|
|||
|
|
|||
|
sandbox.schedule(pattern, callback);
|
|||
|
}, date.getTime() - nowdate.getTime());
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
sandbox.setTimeout(function () {
|
|||
|
try {
|
|||
|
callback.call(sandbox);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
// Reschedule in 2 seconds
|
|||
|
sandbox.setTimeout(function () {
|
|||
|
if (sandbox.__engine.__schedules > 0) sandbox.__engine.__schedules--;
|
|||
|
sandbox.schedule(pattern, callback);
|
|||
|
}, 2000);
|
|||
|
|
|||
|
}, ts.getTime() - nowdate.getTime());
|
|||
|
|
|||
|
if (sandbox.verbose) sandbox.log('schedule(astro=' + pattern.astro + ', offset=' + pattern.shift + ')', 'info');
|
|||
|
|
|||
|
} else {
|
|||
|
// fix problem with sunday and 7
|
|||
|
if (typeof pattern === 'string') {
|
|||
|
const parts = pattern.replace(/\s+/g, ' ').split(' ');
|
|||
|
if (parts.length >= 5 && parts[5] >= 7) parts[5] = 0;
|
|||
|
pattern = parts.join(' ');
|
|||
|
}
|
|||
|
const schedule = nodeSchedule.scheduleJob(pattern, function () {
|
|||
|
try {
|
|||
|
callback.call(sandbox);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
script.schedules.push(schedule);
|
|||
|
|
|||
|
if (sandbox.verbose) sandbox.log('schedule(cron=' + pattern + ')', 'info');
|
|||
|
|
|||
|
return schedule;
|
|||
|
}
|
|||
|
},
|
|||
|
getAstroDate: function (pattern, date, offsetMinutes) {
|
|||
|
if (date === undefined) date = new Date();
|
|||
|
|
|||
|
if (consts.astroList.indexOf(pattern) === -1) {
|
|||
|
const pos = consts.astroListLow.indexOf(pattern.toLowerCase());
|
|||
|
if (pos !== -1) pattern = consts.astroList[pos];
|
|||
|
}
|
|||
|
|
|||
|
if ((!adapter.config.latitude && adapter.config.latitude !== 0) ||
|
|||
|
(!adapter.config.longitude && adapter.config.longitude !== 0)) {
|
|||
|
adapter.log.error('Longitude or latitude does not set. Cannot use astro.');
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
let ts = mods.suncalc.getTimes(date, adapter.config.latitude, adapter.config.longitude)[pattern];
|
|||
|
|
|||
|
if (ts === undefined || ts.getTime().toString() === 'NaN') {
|
|||
|
adapter.log.error('Cannot get astro date for "' + pattern + '"');
|
|||
|
}
|
|||
|
|
|||
|
if (sandbox.verbose) sandbox.log('getAstroDate(pattern=' + pattern + ', date=' + date + ') => ' + ts, 'info');
|
|||
|
|
|||
|
if (offsetMinutes !== undefined) {
|
|||
|
ts = new Date(ts.getTime() + (offsetMinutes * 60000));
|
|||
|
}
|
|||
|
return ts;
|
|||
|
},
|
|||
|
isAstroDay: function () {
|
|||
|
const nowDate = new Date();
|
|||
|
const dayBegin = sandbox.getAstroDate('sunrise');
|
|||
|
const dayEnd = sandbox.getAstroDate('sunset');
|
|||
|
|
|||
|
if (dayBegin === undefined || dayEnd === undefined) return;
|
|||
|
|
|||
|
if (sandbox.verbose) sandbox.log('isAstroDay() => ' + (nowDate >= dayBegin && nowDate <= dayEnd), 'info');
|
|||
|
|
|||
|
return (nowDate >= dayBegin && nowDate <= dayEnd);
|
|||
|
},
|
|||
|
clearSchedule: function (schedule) {
|
|||
|
for (let i = 0; i < script.schedules.length; i++) {
|
|||
|
if (script.schedules[i] === schedule) {
|
|||
|
if (!nodeSchedule.cancelJob(script.schedules[i])) {
|
|||
|
adapter.log.error('Error by canceling scheduled job');
|
|||
|
}
|
|||
|
delete script.schedules[i];
|
|||
|
script.schedules.splice(i, 1);
|
|||
|
if (sandbox.verbose) sandbox.log('clearSchedule() => cleared', 'info');
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
if (sandbox.verbose) sandbox.log('clearSchedule() => invalid handler', 'warn');
|
|||
|
return false;
|
|||
|
},
|
|||
|
setState: function (id, state, isAck, callback) {
|
|||
|
if (typeof isAck === 'function') {
|
|||
|
callback = isAck;
|
|||
|
isAck = undefined;
|
|||
|
}
|
|||
|
|
|||
|
if (state === null) {
|
|||
|
state = {val: null};
|
|||
|
}
|
|||
|
|
|||
|
if (isAck === true || isAck === false || isAck === 'true' || isAck === 'false') {
|
|||
|
if (typeof state === 'object') {
|
|||
|
state.ack = isAck;
|
|||
|
} else {
|
|||
|
state = {val: state, ack: isAck};
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Check type of state
|
|||
|
if (!objects[id] && objects[adapter.namespace + '.' + id]) {
|
|||
|
id = adapter.namespace + '.' + id;
|
|||
|
}
|
|||
|
|
|||
|
const common = objects[id] ? objects[id].common : null;
|
|||
|
if (common &&
|
|||
|
common.type &&
|
|||
|
common.type !== 'mixed' &&
|
|||
|
common.type !== 'file' &&
|
|||
|
common.type !== 'json') {
|
|||
|
if (state && typeof state === 'object' && state.val !== undefined) {
|
|||
|
if (common.type !== typeof state.val) {
|
|||
|
context.logWithLineInfo.warn('Wrong type of ' + id + ': "' + typeof state.val + '". Please fix, while deprecated and will not work in next versions.');
|
|||
|
//return;
|
|||
|
}
|
|||
|
} else {
|
|||
|
if (common.type !== typeof state) {
|
|||
|
context.logWithLineInfo.warn('Wrong type of ' + id + ': "' + typeof state + '". Please fix, while deprecated and will not work in next versions.');
|
|||
|
//return;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
// Check min and max of value
|
|||
|
if (typeof state === 'object' && state) {
|
|||
|
if (common && typeof state.val === 'number') {
|
|||
|
if (common.min !== undefined && state.val < common.min) state.val = common.min;
|
|||
|
if (common.max !== undefined && state.val > common.max) state.val = common.max;
|
|||
|
}
|
|||
|
} else if (common && typeof state === 'number') {
|
|||
|
if (common.min !== undefined && state < common.min) state = common.min;
|
|||
|
if (common.max !== undefined && state > common.max) state = common.max;
|
|||
|
}
|
|||
|
|
|||
|
if (objects[id]) {
|
|||
|
if (sandbox.verbose) sandbox.log('setForeignState(id=' + id + ', state=' + JSON.stringify(state) + ')', 'info');
|
|||
|
|
|||
|
if (debug) {
|
|||
|
sandbox.log('setForeignState(id=' + id + ', state=' + JSON.stringify(state) + ') - ' + words._('was not executed, while debug mode is active'), 'warn');
|
|||
|
|
|||
|
if (typeof callback === 'function') {
|
|||
|
setTimeout(function () {
|
|||
|
try {
|
|||
|
callback.call(sandbox);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}, 0);
|
|||
|
}
|
|||
|
} else {
|
|||
|
adapter.setForeignState(id, state, function (err) {
|
|||
|
if (err) sandbox.log('setForeignState: ' + err, 'error');
|
|||
|
|
|||
|
if (typeof callback === 'function') {
|
|||
|
try {
|
|||
|
callback.call(sandbox);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
} else if (objects[adapter.namespace + '.' + id]) {
|
|||
|
if (sandbox.verbose) sandbox.log('setState(id=' + id + ', state=' + JSON.stringify(state) + ')', 'info');
|
|||
|
|
|||
|
if (debug) {
|
|||
|
sandbox.log('setState(' + id + ', ' + JSON.stringify(state) + ') - ' + words._('was not executed, while debug mode is active'), 'warn');
|
|||
|
if (typeof callback === 'function') {
|
|||
|
setTimeout(function () {
|
|||
|
try {
|
|||
|
callback.call(sandbox);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}, 0);
|
|||
|
}
|
|||
|
} else {
|
|||
|
adapter.setState(id, state, function (err) {
|
|||
|
if (err) sandbox.log('setState: ' + err, 'error');
|
|||
|
|
|||
|
if (typeof callback === 'function') {
|
|||
|
try {
|
|||
|
callback.call(sandbox);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
} else {
|
|||
|
if (objects[id]) {
|
|||
|
if (objects[id].type === 'state') {
|
|||
|
if (sandbox.verbose) sandbox.log('setForeignState(id=' + id + ', state=' + JSON.stringify(state) + ')', 'info');
|
|||
|
|
|||
|
if (debug) {
|
|||
|
sandbox.log('setForeignState(id=' + id + ', state=' + JSON.stringify(state) + ') - ' + words._('was not executed, while debug mode is active'), 'warn');
|
|||
|
if (typeof callback === 'function') {
|
|||
|
setTimeout(function () {
|
|||
|
try {
|
|||
|
callback.call(sandbox);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}, 0);
|
|||
|
}
|
|||
|
} else {
|
|||
|
adapter.setForeignState(id, state, function (err) {
|
|||
|
if (err) sandbox.log('setForeignState: ' + err, 'error');
|
|||
|
|
|||
|
if (typeof callback === 'function') {
|
|||
|
try {
|
|||
|
callback.call(sandbox);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
} else {
|
|||
|
adapter.log.warn('Cannot set value of non-state object "' + id + '"');
|
|||
|
if (typeof callback === 'function') {
|
|||
|
try {
|
|||
|
callback.call(sandbox, 'Cannot set value of non-state object "' + id + '"');
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
} else if (objects[adapter.namespace + '.' + id]) {
|
|||
|
if (objects[adapter.namespace + '.' + id].type === 'state') {
|
|||
|
if (sandbox.verbose) sandbox.log('setState(id=' + id + ', state=' + JSON.stringify(state) + ')', 'info');
|
|||
|
|
|||
|
if (debug) {
|
|||
|
sandbox.log('setState(id=' + id + ', state=' + JSON.stringify(state) + ') - ' + words._('was not executed, while debug mode is active'), 'warn');
|
|||
|
if (typeof callback === 'function') {
|
|||
|
setTimeout(function () {
|
|||
|
try {
|
|||
|
callback.call(sandbox);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}, 0);
|
|||
|
}
|
|||
|
} else {
|
|||
|
adapter.setState(id, state, function (err) {
|
|||
|
if (err) sandbox.log('setState: ' + err, 'error');
|
|||
|
|
|||
|
if (typeof callback === 'function') {
|
|||
|
try {
|
|||
|
callback.call(sandbox);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
} else {
|
|||
|
adapter.log.warn('Cannot set value of non-state object "' + adapter.namespace + '.' + id + '"');
|
|||
|
if (typeof callback === 'function') {
|
|||
|
try {
|
|||
|
callback.call(sandbox, 'Cannot set value of non-state object "' + adapter.namespace + '.' + id + '"');
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
context.logWithLineInfo.warn('State "' + id + '" not found');
|
|||
|
if (typeof callback === 'function') {
|
|||
|
try {
|
|||
|
callback.call(sandbox, 'State "' + id + '" not found');
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
},
|
|||
|
setStateDelayed: function (id, state, isAck, delay, clearRunning, callback) {
|
|||
|
// find arguments
|
|||
|
if (typeof isAck !== 'boolean') {
|
|||
|
callback = clearRunning;
|
|||
|
clearRunning = delay;
|
|||
|
delay = isAck;
|
|||
|
isAck = false;
|
|||
|
}
|
|||
|
if (typeof delay !== 'number') {
|
|||
|
callback = clearRunning;
|
|||
|
clearRunning = delay;
|
|||
|
delay = 0;
|
|||
|
}
|
|||
|
if (typeof clearRunning !== 'boolean') {
|
|||
|
callback = clearRunning;
|
|||
|
clearRunning = true;
|
|||
|
}
|
|||
|
|
|||
|
// Check type of state
|
|||
|
if (!objects[id] && objects[adapter.namespace + '.' + id]) {
|
|||
|
id = adapter.namespace + '.' + id;
|
|||
|
}
|
|||
|
|
|||
|
if (clearRunning === undefined) clearRunning = true;
|
|||
|
|
|||
|
if (sandbox.verbose) sandbox.log('setStateDelayed(id=' + id + ', state=' + state + ', isAck=' + isAck + ', delay=' + delay + ', clearRunning=' + clearRunning + ')', 'info');
|
|||
|
|
|||
|
if (clearRunning) {
|
|||
|
if (timers[id]) {
|
|||
|
if (sandbox.verbose) sandbox.log('setStateDelayed: clear ' + timers[id].length + ' running timers', 'info');
|
|||
|
|
|||
|
for (let i = 0; i < timers[id].length; i++) {
|
|||
|
clearTimeout(timers[id][i].t);
|
|||
|
}
|
|||
|
delete timers[id];
|
|||
|
} else {
|
|||
|
if (sandbox.verbose) sandbox.log('setStateDelayed: no running timers', 'info');
|
|||
|
}
|
|||
|
}
|
|||
|
// If no delay => start immediately
|
|||
|
if (!delay) {
|
|||
|
sandbox.setState(id, state, isAck, callback);
|
|||
|
return null;
|
|||
|
} else {
|
|||
|
// If delay
|
|||
|
timers[id] = timers[id] || [];
|
|||
|
|
|||
|
// calculate timerId
|
|||
|
context.timerId++;
|
|||
|
if (context.timerId > 0xFFFFFFFE) context.timerId = 0;
|
|||
|
|
|||
|
// Start timeout
|
|||
|
const timer = setTimeout(function (_timerId, _id, _state, _isAck) {
|
|||
|
sandbox.setState(_id, _state, _isAck, callback);
|
|||
|
// delete timer handler
|
|||
|
if (timers[_id]) {
|
|||
|
// optimisation
|
|||
|
if (timers[_id].length === 1) {
|
|||
|
delete timers[_id];
|
|||
|
} else {
|
|||
|
for (let t = 0; t < timers[_id].length; t++) {
|
|||
|
if (timers[_id][t].id === _timerId) {
|
|||
|
timers[_id].splice(t, 1);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
if (!timers[_id].length) delete timers[_id];
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
}, delay, context.timerId, id, state, isAck);
|
|||
|
|
|||
|
// add timer handler
|
|||
|
timers[id].push({
|
|||
|
t: timer,
|
|||
|
id: context.timerId,
|
|||
|
ts: Date.now(),
|
|||
|
delay: delay,
|
|||
|
val: typeof state === 'object' && state.val !== undefined ? state.val : state,
|
|||
|
ack: typeof state === 'object' && state.val !== undefined && state.ack !== undefined ? state.ack : isAck
|
|||
|
});
|
|||
|
return context.timerId;
|
|||
|
}
|
|||
|
},
|
|||
|
clearStateDelayed: function (id, timerId) {
|
|||
|
// Check type of state
|
|||
|
if (!objects[id] && objects[adapter.namespace + '.' + id]) {
|
|||
|
id = adapter.namespace + '.' + id;
|
|||
|
}
|
|||
|
|
|||
|
if (sandbox.verbose) sandbox.log('clearStateDelayed(id=' + id + ', timerId=' + timerId + ')', 'info');
|
|||
|
|
|||
|
if (timers[id]) {
|
|||
|
|
|||
|
for (let i = timers[id].length - 1; i >= 0; i--) {
|
|||
|
if (timerId === undefined || timers[id][i].id === timerId) {
|
|||
|
clearTimeout(timers[id][i].t);
|
|||
|
if (timerId !== undefined) timers[id].splice(i, 1);
|
|||
|
if (sandbox.verbose) sandbox.log('clearStateDelayed: clear timer ' + timers[id][i].id, 'info');
|
|||
|
}
|
|||
|
}
|
|||
|
if (timerId === undefined) {
|
|||
|
delete timers[id];
|
|||
|
} else {
|
|||
|
if (!timers[id].length) delete timers[id];
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
return false;
|
|||
|
},
|
|||
|
getStateDelayed: function (id) {
|
|||
|
let result;
|
|||
|
const now = Date.now();
|
|||
|
if (id) {
|
|||
|
// Check type of state
|
|||
|
if (!objects[id] && objects[adapter.namespace + '.' + id]) {
|
|||
|
id = adapter.namespace + '.' + id;
|
|||
|
}
|
|||
|
// If timerId given
|
|||
|
if (typeof id === 'number') {
|
|||
|
for (const _id_ in timers) {
|
|||
|
if (timers.hasOwnProperty(_id_)) {
|
|||
|
for (let ttt = 0; ttt < timers[_id_].length; ttt++) {
|
|||
|
if (timers[_id_][ttt].id === id) {
|
|||
|
return {
|
|||
|
id: _id_,
|
|||
|
left: timers[_id_][ttt].delay - (now - timers[id][ttt].ts),
|
|||
|
delay: timers[_id_][ttt].delay,
|
|||
|
val: timers[_id_][ttt].val,
|
|||
|
ack: timers[_id_][ttt].ack
|
|||
|
};
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
result = [];
|
|||
|
if (timers.hasOwnProperty(id) && timers[id] && timers[id].length) {
|
|||
|
for (let tt = 0; tt < timers[id].length; tt++) {
|
|||
|
result.push({
|
|||
|
timerId: timers[id][tt].id,
|
|||
|
left: timers[id][tt].delay - (now - timers[id][tt].ts),
|
|||
|
delay: timers[id][tt].delay,
|
|||
|
val: timers[id][tt].val,
|
|||
|
ack: timers[id][tt].ack
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
return result;
|
|||
|
} else {
|
|||
|
result = {};
|
|||
|
for (const _id in timers) {
|
|||
|
if (timers.hasOwnProperty(_id) && timers[_id] && timers[_id].length) {
|
|||
|
result[_id] = [];
|
|||
|
for (let t = 0; t < timers[_id].length; t++) {
|
|||
|
result[_id].push({
|
|||
|
timerId: timers[_id][t].id,
|
|||
|
left: timers[_id][t].delay - (now - timers[_id][t].ts),
|
|||
|
delay: timers[_id][t].delay,
|
|||
|
val: timers[_id][t].val,
|
|||
|
ack: timers[_id][t].ack
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return result;
|
|||
|
},
|
|||
|
getState: function (id, callback) {
|
|||
|
if (typeof callback === 'function') {
|
|||
|
if (id.indexOf('.') === -1) {
|
|||
|
adapter.getState(id, callback);
|
|||
|
} else {
|
|||
|
adapter.getForeignState(id, callback);
|
|||
|
}
|
|||
|
} else {
|
|||
|
if (adapter.config.subscribe) {
|
|||
|
sandbox.log('Cannot use sync getState, use callback instead getState("' + id + '", function (err, state){}); or disable the "Do not subscribe all states on start" option in instance configuration.', 'error');
|
|||
|
} else {
|
|||
|
if (states[id]) {
|
|||
|
if (sandbox.verbose) sandbox.log('getState(id=' + id + ', timerId=' + timers[id] + ') => ' + JSON.stringify(states[id]), 'info');
|
|||
|
return states[id];
|
|||
|
}
|
|||
|
if (states[adapter.namespace + '.' + id]) {
|
|||
|
if (sandbox.verbose) sandbox.log('getState(id=' + id + ', timerId=' + timers[id] + ') => ' + states[adapter.namespace + '.' + id], 'info');
|
|||
|
return states[adapter.namespace + '.' + id];
|
|||
|
}
|
|||
|
|
|||
|
if (sandbox.verbose) sandbox.log('getState(id=' + id + ', timerId=' + timers[id] + ') => not found', 'info');
|
|||
|
|
|||
|
context.logWithLineInfo.warn('getState "' + id + '" not found (3)' + (states[id] !== undefined ? ' states[id]=' + states[id] : '')); ///xxx
|
|||
|
return {val: null, notExist: true};
|
|||
|
}
|
|||
|
}
|
|||
|
},
|
|||
|
existsState: function (id) {
|
|||
|
return states.get(id) !== undefined;
|
|||
|
},
|
|||
|
existsObject: function (id) {
|
|||
|
return objects.get(id) !== undefined;
|
|||
|
},
|
|||
|
getIdByName: function (name, alwaysArray) {
|
|||
|
if (sandbox.verbose) sandbox.log('getIdByName(name=' + name + ', alwaysArray=' + alwaysArray + ') => ' + context.names[name], 'info');
|
|||
|
if (alwaysArray) {
|
|||
|
if (typeof context.names[name] === 'string') {
|
|||
|
return [context.names[name]];
|
|||
|
}
|
|||
|
return context.names[name];
|
|||
|
} else {
|
|||
|
return context.names[name];
|
|||
|
}
|
|||
|
},
|
|||
|
getObject: function (id, enumName, cb) {
|
|||
|
if (typeof enumName === 'function') {
|
|||
|
cb = enumName;
|
|||
|
enumName = null;
|
|||
|
}
|
|||
|
if (typeof cb === 'function') {
|
|||
|
adapter.getForeignObject(id, (err, obj) => {
|
|||
|
if (obj) {
|
|||
|
objects[id] = obj;
|
|||
|
} else if (objects[id]) {
|
|||
|
delete objects[id];
|
|||
|
}
|
|||
|
let result;
|
|||
|
try {
|
|||
|
result = JSON.parse(JSON.stringify(objects[id]));
|
|||
|
} catch (err) {
|
|||
|
adapter.log.error('Object "' + id + '" can\'t be copied');
|
|||
|
return null;
|
|||
|
}
|
|||
|
if (sandbox.verbose) sandbox.log('getObject(id=' + id + ', enumName=' + enumName + ') => ' + JSON.stringify(result), 'info');
|
|||
|
cb(err, result);
|
|||
|
});
|
|||
|
} else {
|
|||
|
if (!objects[id]) {
|
|||
|
if (sandbox.verbose) sandbox.log('getObject(id=' + id + ', enumName=' + enumName + ') => does not exist', 'info');
|
|||
|
adapter.log.warn('Object "' + id + '" does not exist');
|
|||
|
return null;
|
|||
|
} else if (enumName) {
|
|||
|
const e = eventObj.getObjectEnumsSync(context, id);
|
|||
|
const obj = JSON.parse(JSON.stringify(objects[id]));
|
|||
|
obj.enumIds = JSON.parse(JSON.stringify(e.enumIds));
|
|||
|
obj.enumNames = JSON.parse(JSON.stringify(e.enumNames));
|
|||
|
if (typeof enumName === 'string') {
|
|||
|
const r = new RegExp('^enum\\.' + enumName + '\\.');
|
|||
|
for (let i = obj.enumIds.length - 1; i >= 0; i--) {
|
|||
|
if (!r.test(obj.enumIds[i])) {
|
|||
|
obj.enumIds.splice(i, 1);
|
|||
|
obj.enumNames.splice(i, 1);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
if (sandbox.verbose) sandbox.log('getObject(id=' + id + ', enumName=' + enumName + ') => ' + JSON.stringify(obj), 'info');
|
|||
|
|
|||
|
return obj;
|
|||
|
} else {
|
|||
|
let result;
|
|||
|
try {
|
|||
|
result = JSON.parse(JSON.stringify(objects[id]));
|
|||
|
} catch (err) {
|
|||
|
adapter.log.error('Object "' + id + '" can\'t be copied');
|
|||
|
return null;
|
|||
|
}
|
|||
|
if (sandbox.verbose) sandbox.log('getObject(id=' + id + ', enumName=' + enumName + ') => ' + JSON.stringify(result), 'info');
|
|||
|
return result;
|
|||
|
}
|
|||
|
}
|
|||
|
},
|
|||
|
setObject: function (id, obj, callback) {
|
|||
|
adapter.log.error('Function "setObject" is not allowed. Use adapter settings to allow it.');
|
|||
|
if (typeof callback === 'function') {
|
|||
|
try {
|
|||
|
callback.call(sandbox, 'Function "setObject" is not allowed. Use adapter settings to allow it.');
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}
|
|||
|
},
|
|||
|
extendObject: function (id, obj, callback) {
|
|||
|
adapter.log.error('Function "extendObject" is not allowed. Use adapter settings to allow it.');
|
|||
|
if (typeof callback === 'function') {
|
|||
|
try {
|
|||
|
callback.call(sandbox, 'Function "extendObject" is not allowed. Use adapter settings to allow it.');
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}
|
|||
|
},
|
|||
|
getEnums: function (enumName) {
|
|||
|
const result = [];
|
|||
|
const r = enumName ? new RegExp('^enum\\.' + enumName + '\\.') : false;
|
|||
|
for (let i = 0; i < enums.length; i++) {
|
|||
|
if (!r || r.test(enums[i])) {
|
|||
|
result.push({
|
|||
|
id: enums[i],
|
|||
|
members: (objects[enums[i]].common) ? objects[enums[i]].common.members : [],
|
|||
|
name: objects[enums[i]].common.name
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
if (sandbox.verbose) sandbox.log('getEnums(enumName=' + enumName + ') => ' + JSON.stringify(result), 'info');
|
|||
|
return JSON.parse(JSON.stringify(result));
|
|||
|
},
|
|||
|
createState: function (name, initValue, forceCreation, common, native, callback) {
|
|||
|
if (typeof native === 'function') {
|
|||
|
callback = native;
|
|||
|
native = {};
|
|||
|
}
|
|||
|
if (typeof common === 'function') {
|
|||
|
callback = common;
|
|||
|
common = undefined;
|
|||
|
}
|
|||
|
if (typeof initValue === 'function') {
|
|||
|
callback = initValue;
|
|||
|
initValue = undefined;
|
|||
|
}
|
|||
|
if (typeof forceCreation === 'function') {
|
|||
|
callback = forceCreation;
|
|||
|
forceCreation = undefined;
|
|||
|
}
|
|||
|
if (typeof initValue === 'object') {
|
|||
|
common = initValue;
|
|||
|
native = forceCreation;
|
|||
|
forceCreation = undefined;
|
|||
|
initValue = undefined;
|
|||
|
}
|
|||
|
if (typeof forceCreation === 'object') {
|
|||
|
common = forceCreation;
|
|||
|
native = common;
|
|||
|
forceCreation = undefined;
|
|||
|
}
|
|||
|
common = common || {};
|
|||
|
common.name = common.name || name;
|
|||
|
common.role = common.role || 'javascript';
|
|||
|
common.type = common.type || 'mixed';
|
|||
|
if (initValue === undefined) initValue = common.def;
|
|||
|
|
|||
|
native = native || {};
|
|||
|
|
|||
|
// Check min, max and def values for number
|
|||
|
if (common.type !== undefined && common.type === 'number') {
|
|||
|
let min = 0;
|
|||
|
let max = 0;
|
|||
|
let def = 0;
|
|||
|
let err;
|
|||
|
if (common.min !== undefined) {
|
|||
|
min = common.min;
|
|||
|
if (typeof min !== 'number') {
|
|||
|
min = parseFloat(min);
|
|||
|
if (isNaN(min)) {
|
|||
|
err = 'Wrong type of ' + name + '.common.min';
|
|||
|
sandbox.log(err, 'error');
|
|||
|
if (typeof callback === 'function') {
|
|||
|
try {
|
|||
|
callback.call(sandbox, err);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}
|
|||
|
return;
|
|||
|
} else {
|
|||
|
common.min = min;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
if (common.max !== undefined) {
|
|||
|
max = common.max;
|
|||
|
if (typeof max !== 'number') {
|
|||
|
max = parseFloat(max);
|
|||
|
if (isNaN(max)) {
|
|||
|
err = 'Wrong type of ' + name + '.common.max';
|
|||
|
sandbox.log(err, 'error');
|
|||
|
if (typeof callback === 'function') {
|
|||
|
try {
|
|||
|
callback.call(sandbox, err);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}
|
|||
|
return;
|
|||
|
} else {
|
|||
|
common.max = max;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
if (common.def !== undefined) {
|
|||
|
def = common.def;
|
|||
|
if (typeof def !== 'number') {
|
|||
|
def = parseFloat(def);
|
|||
|
if (isNaN(def)) {
|
|||
|
err = 'Wrong type of ' + name + '.common.def';
|
|||
|
sandbox.log(err, 'error');
|
|||
|
if (typeof callback === 'function') {
|
|||
|
try {
|
|||
|
callback.call(sandbox, err);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}
|
|||
|
return;
|
|||
|
} else {
|
|||
|
common.def = def;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
if (common.min !== undefined && common.max !== undefined && min > max) {
|
|||
|
common.max = min;
|
|||
|
common.min = max;
|
|||
|
}
|
|||
|
if (common.def !== undefined && common.min !== undefined && def < min) common.def = min;
|
|||
|
if (common.def !== undefined && common.max !== undefined && def > max) common.def = max;
|
|||
|
}
|
|||
|
|
|||
|
if (sandbox.verbose) sandbox.log('createState(name=' + name + ', initValue=' + initValue + ', forceCreation=' + forceCreation + ', common=' + JSON.stringify(common) + ', native=' + JSON.stringify(native) + ')', 'debug');
|
|||
|
|
|||
|
if (forceCreation) {
|
|||
|
// todo: store object in objects to have this object directly after callback
|
|||
|
adapter.setObject(name, {
|
|||
|
common: common,
|
|||
|
native: native,
|
|||
|
type: 'state'
|
|||
|
}, function (err) {
|
|||
|
if (err) adapter.log.warn('Cannot set object "' + name + '": ' + err);
|
|||
|
|
|||
|
if (initValue !== undefined) {
|
|||
|
if (typeof initValue === 'object' && initValue.ack !== undefined) {
|
|||
|
adapter.setState(name, initValue, callback);
|
|||
|
} else {
|
|||
|
adapter.setState(name, initValue, true, callback);
|
|||
|
}
|
|||
|
} else {
|
|||
|
if (typeof callback === 'function') {
|
|||
|
try {
|
|||
|
callback.call(sandbox, name);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
} else {
|
|||
|
adapter.getObject(name, function (err, obj) {
|
|||
|
if (err || !obj) {
|
|||
|
// todo: store object in objects to have this object directly after callback
|
|||
|
// create new one
|
|||
|
if (name.match(/^javascript\.\d+\./)) {
|
|||
|
adapter.setForeignObject(name, {
|
|||
|
common: common,
|
|||
|
native: native,
|
|||
|
type: 'state'
|
|||
|
}, function (err) {
|
|||
|
if (err) adapter.log.warn('Cannot set object "' + name + '": ' + err);
|
|||
|
|
|||
|
if (initValue !== undefined) {
|
|||
|
adapter.setForeignState(name, initValue, callback);
|
|||
|
if (typeof initValue === 'object' && initValue.ack !== undefined) {
|
|||
|
adapter.setForeignState(name, initValue, callback);
|
|||
|
} else {
|
|||
|
adapter.setForeignState(name, initValue, true, callback);
|
|||
|
}
|
|||
|
} else {
|
|||
|
adapter.setForeignState(name, null, true, callback);
|
|||
|
}
|
|||
|
});
|
|||
|
} else {
|
|||
|
adapter.setObject(name, {
|
|||
|
common: common,
|
|||
|
native: native,
|
|||
|
type: 'state'
|
|||
|
}, function (err) {
|
|||
|
if (err) adapter.log.warn('Cannot set object "' + name + '": ' + err);
|
|||
|
|
|||
|
if (initValue !== undefined) {
|
|||
|
if (typeof initValue === 'object' && initValue.ack !== undefined) {
|
|||
|
adapter.setState(name, initValue, callback);
|
|||
|
} else {
|
|||
|
adapter.setState(name, initValue, true, callback);
|
|||
|
}
|
|||
|
} else {
|
|||
|
adapter.setState(name, null, true, callback);
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
}
|
|||
|
} else {
|
|||
|
if (!adapter.config.subscribe && !states[name] && !states[adapter.namespace + '.' + name]) {
|
|||
|
if (name.substring(0, adapter.namespace.length) !== adapter.namespace) {
|
|||
|
states[adapter.namespace + '.' + name] = {val: null, ack: true};
|
|||
|
} else {
|
|||
|
states[name] = {val: null, ack: true};
|
|||
|
}
|
|||
|
}
|
|||
|
// state yet exists
|
|||
|
if (typeof callback === 'function') {
|
|||
|
try {
|
|||
|
callback.call(sandbox, name);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
},
|
|||
|
deleteState: function (id, callback) {
|
|||
|
// todo: check rights
|
|||
|
let found = false;
|
|||
|
if (objects[id]) {
|
|||
|
found = true;
|
|||
|
delete objects[id];
|
|||
|
}
|
|||
|
if (states[id]) delete states[id];
|
|||
|
if (objects[adapter.namespace + '.' + id]) {
|
|||
|
delete objects[adapter.namespace + '.' + id];
|
|||
|
found = true;
|
|||
|
}
|
|||
|
if (states[adapter.namespace + '.' + id]) delete states[adapter.namespace + '.' + id];
|
|||
|
|
|||
|
if (sandbox.verbose) sandbox.log('deleteState(id=' + id + ')', 'debug');
|
|||
|
adapter.delObject(id, function (err) {
|
|||
|
if (err) adapter.log.warn('Object for state "' + id + '" does not exist: ' + err);
|
|||
|
|
|||
|
adapter.delState(id, function (err) {
|
|||
|
if (err) adapter.log.error('Cannot delete state "' + id + '": ' + err);
|
|||
|
if (typeof callback === 'function') {
|
|||
|
if (typeof callback === 'function') {
|
|||
|
try {
|
|||
|
callback.call(sandbox, err, found);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
});
|
|||
|
},
|
|||
|
sendTo: function (_adapter, cmd, msg, callback) {
|
|||
|
if (sandbox.verbose) sandbox.log('sendTo(adapter=' + _adapter + ', cmd=' + cmd + ', msg=' + JSON.stringify(msg) + ')', 'info');
|
|||
|
adapter.sendTo(_adapter, cmd, msg, callback);
|
|||
|
},
|
|||
|
sendto: function (_adapter, cmd, msg, callback) {
|
|||
|
return sandbox.sendTo(_adapter, cmd, msg, callback);
|
|||
|
},
|
|||
|
sendToHost: function (host, cmd, msg, callback) {
|
|||
|
if (!adapter.config.enableSendToHost) {
|
|||
|
const error = 'sendToHost is not available. Please enable "Enable SendToHost" option in instance settings';
|
|||
|
adapter.log.error(error);
|
|||
|
sandbox.log(error);
|
|||
|
if (typeof callback === 'function') {
|
|||
|
setImmediate(function () {
|
|||
|
callback(error);
|
|||
|
});
|
|||
|
}
|
|||
|
} else {
|
|||
|
if (sandbox.verbose) sandbox.log('sendToHost(adapter=' + host + ', cmd=' + cmd + ', msg=' + JSON.stringify(msg) + ')', 'info');
|
|||
|
adapter.sendToHost(host, cmd, msg, callback);
|
|||
|
}
|
|||
|
},
|
|||
|
setInterval: function (callback, ms, arg1, arg2, arg3, arg4) {
|
|||
|
if (typeof callback === 'function') {
|
|||
|
const int = setInterval(function (_arg1, _arg2, _arg3, _arg4) {
|
|||
|
try {
|
|||
|
callback.call(sandbox, _arg1, _arg2, _arg3, _arg4);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}, ms, arg1, arg2, arg3, arg4);
|
|||
|
script.intervals.push(int);
|
|||
|
|
|||
|
if (sandbox.verbose) sandbox.log('setInterval(ms=' + ms + ')', 'info');
|
|||
|
return int;
|
|||
|
} else {
|
|||
|
sandbox.log('Invalid callback for setInterval! - ' + typeof callback, 'error');
|
|||
|
return null;
|
|||
|
}
|
|||
|
},
|
|||
|
clearInterval: function (id) {
|
|||
|
const pos = script.intervals.indexOf(id);
|
|||
|
if (pos !== -1) {
|
|||
|
if (sandbox.verbose) sandbox.log('clearInterval() => cleared', 'info');
|
|||
|
clearInterval(id);
|
|||
|
script.intervals.splice(pos, 1);
|
|||
|
} else {
|
|||
|
if (sandbox.verbose) sandbox.log('clearInterval() => not found', 'warn');
|
|||
|
}
|
|||
|
},
|
|||
|
setTimeout: function (callback, ms, arg1, arg2, arg3, arg4) {
|
|||
|
if (typeof callback === 'function') {
|
|||
|
const to = setTimeout(function (_arg1, _arg2, _arg3, _arg4) {
|
|||
|
// Remove timeout from the list
|
|||
|
const pos = script.timeouts.indexOf(to);
|
|||
|
if (pos !== -1) script.timeouts.splice(pos, 1);
|
|||
|
|
|||
|
try {
|
|||
|
callback.call(sandbox, _arg1, _arg2, _arg3, _arg4);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}, ms, arg1, arg2, arg3, arg4);
|
|||
|
if (sandbox.verbose) sandbox.log('setTimeout(ms=' + ms + ')', 'info');
|
|||
|
|
|||
|
script.timeouts.push(to);
|
|||
|
return to;
|
|||
|
} else {
|
|||
|
sandbox.log('Invalid callback for setTimeout! - ' + typeof callback, 'error');
|
|||
|
return null;
|
|||
|
}
|
|||
|
},
|
|||
|
clearTimeout: function (id) {
|
|||
|
const pos = script.timeouts.indexOf(id);
|
|||
|
if (pos !== -1) {
|
|||
|
if (sandbox.verbose) sandbox.log('clearTimeout() => cleared', 'info');
|
|||
|
clearTimeout(id);
|
|||
|
script.timeouts.splice(pos, 1);
|
|||
|
} else {
|
|||
|
if (sandbox.verbose) sandbox.log('clearTimeout() => not found', 'warn');
|
|||
|
}
|
|||
|
},
|
|||
|
setImmediate: function (callback, arg1, arg2, arg3, arg4, arg5) {
|
|||
|
if (typeof callback === 'function') {
|
|||
|
setImmediate(function (_arg1, _arg2, _arg3, _arg4, _arg5) {
|
|||
|
try {
|
|||
|
callback.call(sandbox, _arg1, _arg2, _arg3, _arg4, _arg5);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e);
|
|||
|
}
|
|||
|
}, arg1, arg2, arg3, arg4, arg5);
|
|||
|
if (sandbox.verbose) sandbox.log('setImmediate()', 'info');
|
|||
|
} else {
|
|||
|
sandbox.log('Invalid callback for setImmediate! - ' + typeof callback, 'error');
|
|||
|
}
|
|||
|
},
|
|||
|
cb: function (callback) {
|
|||
|
return function () {
|
|||
|
if (context.scripts[name] && context.scripts[name]._id === sandbox._id) {
|
|||
|
if (typeof callback === 'function') {
|
|||
|
try {
|
|||
|
callback.apply(this, arguments);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
adapter.log.warn('Callback for old version of script: ' + name);
|
|||
|
}
|
|||
|
};
|
|||
|
},
|
|||
|
compareTime: function (startTime, endTime, operation, time) {
|
|||
|
let pos;
|
|||
|
if (startTime && typeof startTime === 'string') {
|
|||
|
if ((pos = consts.astroListLow.indexOf(startTime.toLowerCase())) !== -1) {
|
|||
|
startTime = sandbox.getAstroDate(consts.astroList[pos]);
|
|||
|
startTime = startTime.toLocaleTimeString([], {
|
|||
|
hour: '2-digit',
|
|||
|
minute: '2-digit',
|
|||
|
hour12: false
|
|||
|
});
|
|||
|
}
|
|||
|
} else if (startTime && typeof startTime === 'object' && startTime.astro) {
|
|||
|
startTime = sandbox.getAstroDate(startTime.astro, startTime.date || new Date(), startTime.offset || 0);
|
|||
|
startTime = startTime.toLocaleTimeString([], {
|
|||
|
hour: '2-digit',
|
|||
|
minute: '2-digit',
|
|||
|
hour12: false
|
|||
|
});
|
|||
|
}
|
|||
|
if (endTime && typeof endTime === 'string') {
|
|||
|
if ((pos = consts.astroListLow.indexOf(endTime.toLowerCase())) !== -1) {
|
|||
|
endTime = sandbox.getAstroDate(consts.astroList[pos]);
|
|||
|
endTime = endTime.toLocaleTimeString([], {
|
|||
|
hour: '2-digit',
|
|||
|
minute: '2-digit',
|
|||
|
hour12: false
|
|||
|
});
|
|||
|
}
|
|||
|
} else if (endTime && typeof endTime === 'object' && endTime.astro) {
|
|||
|
endTime = sandbox.getAstroDate(endTime.astro, endTime.date || new Date(), endTime.offset || 0);
|
|||
|
endTime = endTime.toLocaleTimeString([], {
|
|||
|
hour: '2-digit',
|
|||
|
minute: '2-digit',
|
|||
|
hour12: false
|
|||
|
});
|
|||
|
}
|
|||
|
if (time && typeof time === 'string') {
|
|||
|
if ((pos = consts.astroListLow.indexOf(time.toLowerCase())) !== -1) {
|
|||
|
time = sandbox.getAstroDate(consts.astroList[pos]);
|
|||
|
}
|
|||
|
} else if (time && typeof time === 'object' && time.astro) {
|
|||
|
time = sandbox.getAstroDate(time.astro, time.date || new Date(), time.offset || 0);
|
|||
|
}
|
|||
|
|
|||
|
let daily = true;
|
|||
|
if (time) {
|
|||
|
daily = false;
|
|||
|
}
|
|||
|
if (time && typeof time !== 'object') {
|
|||
|
if (typeof time === 'string' && time.indexOf(' ') === -1 && time.indexOf('T') === -1) {
|
|||
|
const parts = time.split(':');
|
|||
|
time = new Date();
|
|||
|
time.setHours(parseInt(parts[0], 10));
|
|||
|
time.setMinutes(parseInt(parts[1], 10));
|
|||
|
time.setMilliseconds(0);
|
|||
|
|
|||
|
if (parts.length === 3) {
|
|||
|
time.setSeconds(parseInt(parts[2], 10));
|
|||
|
} else {
|
|||
|
time.setSeconds(0);
|
|||
|
}
|
|||
|
} else {
|
|||
|
time = new Date(time);
|
|||
|
}
|
|||
|
} else if (!time) {
|
|||
|
time = new Date();
|
|||
|
time.setMilliseconds(0);
|
|||
|
}
|
|||
|
|
|||
|
if (typeof startTime === 'string') {
|
|||
|
if (startTime.indexOf(' ') === -1 && startTime.indexOf('T') === -1) {
|
|||
|
const parts = startTime.split(':');
|
|||
|
startTime = new Date();
|
|||
|
startTime.setHours(parseInt(parts[0], 10));
|
|||
|
startTime.setMinutes(parseInt(parts[1], 10));
|
|||
|
startTime.setMilliseconds(0);
|
|||
|
|
|||
|
if (parts.length === 3) {
|
|||
|
startTime.setSeconds(parseInt(parts[2], 10));
|
|||
|
} else {
|
|||
|
startTime.setSeconds(0);
|
|||
|
}
|
|||
|
} else {
|
|||
|
daily = false;
|
|||
|
startTime = new Date(startTime);
|
|||
|
}
|
|||
|
} else {
|
|||
|
daily = false;
|
|||
|
startTime = new Date(startTime);
|
|||
|
}
|
|||
|
startTime = startTime.getTime();
|
|||
|
|
|||
|
if (endTime && typeof endTime === 'string') {
|
|||
|
if (endTime.indexOf(' ') === -1 && endTime.indexOf('T') === -1) {
|
|||
|
const parts = endTime.split(':');
|
|||
|
endTime = new Date();
|
|||
|
endTime.setHours(parseInt(parts[0], 10));
|
|||
|
endTime.setMinutes(parseInt(parts[1], 10));
|
|||
|
endTime.setMilliseconds(0);
|
|||
|
|
|||
|
if (parts.length === 3) {
|
|||
|
endTime.setSeconds(parseInt(parts[2], 10));
|
|||
|
} else {
|
|||
|
endTime.setSeconds(0);
|
|||
|
}
|
|||
|
} else {
|
|||
|
daily = false;
|
|||
|
endTime = new Date(endTime);
|
|||
|
}
|
|||
|
} else if (endTime) {
|
|||
|
daily = false;
|
|||
|
endTime = new Date(endTime);
|
|||
|
} else {
|
|||
|
endTime = null;
|
|||
|
}
|
|||
|
|
|||
|
if (endTime) endTime = endTime.getTime();
|
|||
|
|
|||
|
if (operation === 'between') {
|
|||
|
if (endTime) {
|
|||
|
if (startTime > endTime && daily) return !(time >= endTime && time < startTime);
|
|||
|
else return time >= startTime && time < endTime;
|
|||
|
} else {
|
|||
|
adapter.log.warn('missing or unrecognized endTime expression: ' + endTime);
|
|||
|
return false;
|
|||
|
}
|
|||
|
} else if (operation === 'not between') {
|
|||
|
if (endTime) {
|
|||
|
if (startTime > endTime && daily) return time >= endTime && time < startTime;
|
|||
|
else return !(time >= startTime && time < endTime);
|
|||
|
} else {
|
|||
|
adapter.log.warn('missing or unrecognized endTime expression: ' + endTime);
|
|||
|
return false;
|
|||
|
}
|
|||
|
} else if (operation === '>') {
|
|||
|
return time > startTime;
|
|||
|
} else if (operation === '>=') {
|
|||
|
return time >= startTime;
|
|||
|
} else if (operation === '<') {
|
|||
|
return time < startTime;
|
|||
|
} else if (operation === '<=') {
|
|||
|
return time <= startTime;
|
|||
|
} else if (operation === '==') {
|
|||
|
return time === startTime;
|
|||
|
} else if (operation === '<>') {
|
|||
|
return time !== startTime;
|
|||
|
} else {
|
|||
|
adapter.log.warn('Invalid operator: ' + operation);
|
|||
|
return false;
|
|||
|
}
|
|||
|
},
|
|||
|
onStop: function (cb, timeout) {
|
|||
|
if (sandbox.verbose) sandbox.log('onStop(timeout=' + timeout + ')', 'info');
|
|||
|
|
|||
|
script.onStopCb = cb;
|
|||
|
script.onStopTimeout = timeout || 1000;
|
|||
|
},
|
|||
|
formatValue: function (value, decimals, format) {
|
|||
|
if (!format && objects['system.config']) {
|
|||
|
format = objects['system.config'].common.isFloatComma ? '.,' : ',.';
|
|||
|
}
|
|||
|
return adapter.formatValue(value, decimals, format);
|
|||
|
},
|
|||
|
|
|||
|
formatDate: function (date, format, language) {
|
|||
|
if (!format) {
|
|||
|
format = objects['system.config'] ? (objects['system.config'].common.dateFormat || 'DD.MM.YYYY') : 'DD.MM.YYYY';
|
|||
|
}
|
|||
|
if (format.match(/[WНOО]+/)) {
|
|||
|
let text = adapter.formatDate(date, format);
|
|||
|
if (!language || !consts.dayOfWeeksFull[language]) language = objects['system.config'].common.language;
|
|||
|
const d = date.getDay();
|
|||
|
text = text.replace('WW', consts.dayOfWeeksFull[language][d]);
|
|||
|
text = text.replace('НН', consts.dayOfWeeksFull[language][d]);
|
|||
|
text = text.replace('W', consts.dayOfWeeksShort[language][d]);
|
|||
|
text = text.replace('Н', consts.dayOfWeeksShort[language][d]);
|
|||
|
text = text.replace('W', consts.dayOfWeeksShort[language][d]);
|
|||
|
text = text.replace('Н', consts.dayOfWeeksShort[language][d]);
|
|||
|
const m = date.getMonth();
|
|||
|
text = text.replace('OOO', consts.monthFullGen[language][m]);
|
|||
|
text = text.replace('ООО', consts.monthFullGen[language][m]);
|
|||
|
text = text.replace('OO', consts.monthFull[language][m]);
|
|||
|
text = text.replace('ОО', consts.monthFull[language][m]);
|
|||
|
text = text.replace('O', consts.monthShort[language][m]);
|
|||
|
text = text.replace('О', consts.monthShort[language][m]);
|
|||
|
text = text.replace('O', consts.monthShort[language][m]);
|
|||
|
text = text.replace('О', consts.monthShort[language][m]);
|
|||
|
return text;
|
|||
|
} else {
|
|||
|
return adapter.formatDate(date, format);
|
|||
|
}
|
|||
|
},
|
|||
|
|
|||
|
getDateObject: function (date) {
|
|||
|
if (typeof date === 'object') return date;
|
|||
|
if (typeof date !== 'string') return new Date(date);
|
|||
|
if (date.match(/^\d?\d$/)) {
|
|||
|
const _now = new Date();
|
|||
|
date = _now.getFullYear() + '-' + (_now.getMonth() + 1) + '-' + _now.getDate() + ' ' + date + ':00';
|
|||
|
} else {
|
|||
|
// 20:00, 2:00, 20:00:00, 2:00:00
|
|||
|
if (date.match(/^\d?\d:\d\d(:\d\d)?$/)) {
|
|||
|
const now = new Date();
|
|||
|
date = now.getFullYear() + '-' + (now.getMonth() + 1) + '-' + now.getDate() + ' ' + date;
|
|||
|
}
|
|||
|
}
|
|||
|
return new Date(date);
|
|||
|
},
|
|||
|
|
|||
|
writeFile: function (_adapter, fileName, data, callback) {
|
|||
|
if (typeof data === 'function' || !data) {
|
|||
|
callback = data;
|
|||
|
data = fileName;
|
|||
|
fileName = _adapter;
|
|||
|
_adapter = null;
|
|||
|
}
|
|||
|
|
|||
|
if (debug) {
|
|||
|
sandbox.log('readFile(adapter=' + _adapter + ', fileName=' + fileName + ') - ' + words._('was not executed, while debug mode is active'), 'warn');
|
|||
|
if (typeof callback === 'function') {
|
|||
|
setTimeout(function () {
|
|||
|
try {
|
|||
|
callback.call(sandbox);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}, 0);
|
|||
|
}
|
|||
|
} else {
|
|||
|
if (sandbox.verbose) sandbox.log('readFile(adapter=' + _adapter + ', fileName=' + fileName + ')', 'info');
|
|||
|
adapter.writeFile(_adapter, fileName, data, callback);
|
|||
|
}
|
|||
|
},
|
|||
|
readFile: function (_adapter, fileName, callback) {
|
|||
|
if (typeof fileName === 'function') {
|
|||
|
callback = fileName;
|
|||
|
fileName = _adapter;
|
|||
|
_adapter = null;
|
|||
|
}
|
|||
|
if (sandbox.verbose) sandbox.log('readFile(adapter=' + _adapter + ', fileName=' + fileName + ')', 'info');
|
|||
|
|
|||
|
adapter.readFile(_adapter, fileName, callback);
|
|||
|
},
|
|||
|
unlink: function (_adapter, fileName, callback) {
|
|||
|
if (typeof fileName === 'function') {
|
|||
|
callback = fileName;
|
|||
|
fileName = _adapter;
|
|||
|
_adapter = null;
|
|||
|
}
|
|||
|
if (sandbox.verbose) sandbox.log('unlink(adapter=' + _adapter + ', fileName=' + fileName + ')', 'info');
|
|||
|
|
|||
|
if (debug) {
|
|||
|
sandbox.log('unlink(adapter=' + _adapter + ', fileName=' + fileName + ') - ' + words._('was not executed, while debug mode is active'), 'warn');
|
|||
|
if (typeof callback === 'function') {
|
|||
|
setTimeout(function () {
|
|||
|
try {
|
|||
|
callback.call(sandbox);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}, 0);
|
|||
|
}
|
|||
|
} else {
|
|||
|
if (sandbox.verbose) sandbox.log('unlink(adapter=' + _adapter + ', fileName=' + fileName + ')', 'info');
|
|||
|
adapter.unlink(_adapter, fileName, callback);
|
|||
|
}
|
|||
|
},
|
|||
|
delFile: function (_adapter, fileName, callback) {
|
|||
|
return sandbox.unlink(_adapter, fileName, callback);
|
|||
|
},
|
|||
|
getHistory: function (instance, options, callback) {
|
|||
|
if (typeof instance === 'object') {
|
|||
|
callback = options;
|
|||
|
options = instance;
|
|||
|
instance = null;
|
|||
|
}
|
|||
|
|
|||
|
if (typeof callback !== 'function') {
|
|||
|
adapter.log.error('No callback found!');
|
|||
|
return;
|
|||
|
}
|
|||
|
if (typeof options !== 'object') {
|
|||
|
adapter.log.error('No options found!');
|
|||
|
return;
|
|||
|
}
|
|||
|
if (!options.id) {
|
|||
|
adapter.log.error('No ID found!');
|
|||
|
return;
|
|||
|
}
|
|||
|
const timeoutMs = parseInt(options.timeout, 10) || 20000;
|
|||
|
|
|||
|
if (!instance) {
|
|||
|
instance = objects['system.config'] ? objects['system.config'].common.defaultHistory : null;
|
|||
|
}
|
|||
|
|
|||
|
if (sandbox.verbose) sandbox.log('getHistory(instance=' + instance + ', options=' + JSON.stringify(options) + ')', 'debug');
|
|||
|
|
|||
|
if (!instance) {
|
|||
|
adapter.log.error('No default history instance found!');
|
|||
|
try {
|
|||
|
callback.call(sandbox, 'No default history instance found!');
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
if (instance.match(/^system\.adapter\./)) instance = instance.substring('system.adapter.'.length);
|
|||
|
|
|||
|
if (!objects['system.adapter.' + instance]) {
|
|||
|
adapter.log.error('Instance "' + instance + '" not found!');
|
|||
|
try {
|
|||
|
callback.call(sandbox, 'Instance "' + instance + '" not found!');
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
let timeout = setTimeout(function () {
|
|||
|
timeout = null;
|
|||
|
|
|||
|
if (sandbox.verbose) sandbox.log('getHistory => timeout', 'debug');
|
|||
|
|
|||
|
if (typeof callback === 'function') {
|
|||
|
try {
|
|||
|
callback.call(sandbox, 'Timeout', null, options, instance);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
callback = null;
|
|||
|
}
|
|||
|
}, timeoutMs);
|
|||
|
|
|||
|
adapter.sendTo(instance, 'getHistory', {id: options.id, options: options}, function (result) {
|
|||
|
if (timeout) clearTimeout(timeout);
|
|||
|
|
|||
|
if (sandbox.verbose && result.error) sandbox.log('getHistory => ' + result.error, 'error');
|
|||
|
if (sandbox.verbose && result.result) sandbox.log('getHistory => ' + result.result.length + ' items', 'debug');
|
|||
|
|
|||
|
if (typeof callback === 'function') {
|
|||
|
try {
|
|||
|
callback.call(sandbox, result.error, result.result, options, instance);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
callback = null;
|
|||
|
}
|
|||
|
|
|||
|
});
|
|||
|
},
|
|||
|
runScript: function (scriptName, callback) {
|
|||
|
scriptName = scriptName || name;
|
|||
|
if (!scriptName.match(/^script\.js\./)) scriptName = 'script.js.' + scriptName;
|
|||
|
// start other script
|
|||
|
if (!objects[scriptName] || !objects[scriptName].common) {
|
|||
|
sandbox.log('Cannot start "' + scriptName + '", because not found', 'error');
|
|||
|
return false;
|
|||
|
} else {
|
|||
|
if (debug) {
|
|||
|
sandbox.log('runScript(scriptName=' + scriptName + ') - ' + words._('was not executed, while debug mode is active'), 'warn');
|
|||
|
} else {
|
|||
|
if (objects[scriptName].common.enabled) {
|
|||
|
objects[scriptName].common.enabled = false;
|
|||
|
adapter.extendForeignObject(scriptName, {common: {enabled: false}}, function (/* err, obj */) {
|
|||
|
adapter.extendForeignObject(scriptName, {common: {enabled: true}}, function (err) {
|
|||
|
if (callback === 'function') callback(err);
|
|||
|
});
|
|||
|
scriptName = null;
|
|||
|
});
|
|||
|
} else {
|
|||
|
adapter.extendForeignObject(scriptName, {common: {enabled: true}}, function (err) {
|
|||
|
if (callback === 'function') callback(err);
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
},
|
|||
|
startScript: function (scriptName, ignoreIfStarted, callback) {
|
|||
|
if (typeof ignoreIfStarted === 'function') {
|
|||
|
callback = ignoreIfStarted;
|
|||
|
ignoreIfStarted = false;
|
|||
|
}
|
|||
|
scriptName = scriptName || name;
|
|||
|
if (!scriptName.match(/^script\.js\./)) scriptName = 'script.js.' + scriptName;
|
|||
|
// start other script
|
|||
|
if (!objects[scriptName] || !objects[scriptName].common) {
|
|||
|
sandbox.log('Cannot start "' + scriptName + '", because not found', 'error');
|
|||
|
return false;
|
|||
|
} else {
|
|||
|
console.log('STARTING!');
|
|||
|
if (debug) {
|
|||
|
sandbox.log('startScript(scriptName=' + scriptName + ') - ' + words._('was not executed, while debug mode is active'), 'warn');
|
|||
|
} else {
|
|||
|
if (objects[scriptName].common.enabled) {
|
|||
|
if (!ignoreIfStarted) {
|
|||
|
objects[scriptName].common.enabled = false;
|
|||
|
adapter.extendForeignObject(scriptName, {common: {enabled: false}}, function (err) {
|
|||
|
adapter.extendForeignObject(scriptName, {common: {enabled: true}}, function (err) {
|
|||
|
if (callback === 'function') callback(err, true);
|
|||
|
});
|
|||
|
scriptName = null;
|
|||
|
});
|
|||
|
} else if (callback === 'function') {
|
|||
|
callback(null, false);
|
|||
|
}
|
|||
|
} else {
|
|||
|
adapter.extendForeignObject(scriptName, {common: {enabled: true}}, function (err) {
|
|||
|
if (callback === 'function') callback(err, true);
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
},
|
|||
|
stopScript: function (scriptName, callback) {
|
|||
|
scriptName = scriptName || name;
|
|||
|
|
|||
|
if (!scriptName.match(/^script\.js\./)) scriptName = 'script.js.' + scriptName;
|
|||
|
|
|||
|
// stop other script
|
|||
|
if (!objects[scriptName] || !objects[scriptName].common) {
|
|||
|
sandbox.log('Cannot stop "' + scriptName + '", because not found', 'error');
|
|||
|
return false;
|
|||
|
} else {
|
|||
|
if (debug) {
|
|||
|
sandbox.log('stopScript(scriptName=' + scriptName + ') - ' + words._('was not executed, while debug mode is active'), 'warn');
|
|||
|
} else {
|
|||
|
if (objects[scriptName].common.enabled) {
|
|||
|
objects[scriptName].common.enabled = false;
|
|||
|
adapter.extendForeignObject(scriptName, {common: {enabled: false}}, function (err) {
|
|||
|
if (callback === 'function') callback(err, true);
|
|||
|
scriptName = null;
|
|||
|
});
|
|||
|
} else if (callback === 'function') {
|
|||
|
callback(null, false);
|
|||
|
}
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
},
|
|||
|
isScriptActive: function (scriptName) {
|
|||
|
if (!scriptName.match(/^script\.js\./)) scriptName = 'script.js.' + scriptName;
|
|||
|
if (!objects[scriptName] || !objects[scriptName].common) {
|
|||
|
sandbox.log('Script does not exist', 'error');
|
|||
|
return false;
|
|||
|
} else {
|
|||
|
return objects[scriptName].common.enabled;
|
|||
|
}
|
|||
|
},
|
|||
|
toInt: function (val) {
|
|||
|
if (val === true || val === 'true') val = 1;
|
|||
|
if (val === false || val === 'false') val = 0;
|
|||
|
val = parseInt(val) || 0;
|
|||
|
return val;
|
|||
|
},
|
|||
|
toFloat: function (val) {
|
|||
|
if (val === true || val === 'true') val = 1;
|
|||
|
if (val === false || val === 'false') val = 0;
|
|||
|
val = parseFloat(val) || 0;
|
|||
|
return val;
|
|||
|
},
|
|||
|
toBoolean: function (val) {
|
|||
|
if (val === '1' || val === 'true') val = true;
|
|||
|
if (val === '0' || val === 'false') val = false;
|
|||
|
return !!val;
|
|||
|
},
|
|||
|
getAttr: function (obj, path) {
|
|||
|
if (typeof path === 'string') {
|
|||
|
path = path.split('.');
|
|||
|
}
|
|||
|
if (typeof obj === 'string') {
|
|||
|
try {
|
|||
|
obj = JSON.parse(obj);
|
|||
|
} catch (e) {
|
|||
|
sandbox.log('Cannot parse "' + obj.substring(0, 30) + '"' + e, 'error');
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const attr = path.shift();
|
|||
|
try {
|
|||
|
obj = obj[attr];
|
|||
|
} catch (e) {
|
|||
|
sandbox.log(`Cannot get ${attr} of ${JSON.stringify(obj)}`, 'error');
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
if (!path.length) {
|
|||
|
return obj;
|
|||
|
} else {
|
|||
|
const type = typeof obj;
|
|||
|
if (obj === null || obj === undefined || type === 'boolean' || type === 'number') {
|
|||
|
return null;
|
|||
|
} else {
|
|||
|
return sandbox.getAttr(obj, path);
|
|||
|
}
|
|||
|
}
|
|||
|
},
|
|||
|
console: {
|
|||
|
log: function (msg) {
|
|||
|
sandbox.log(msg, 'info');
|
|||
|
},
|
|||
|
error: function (msg) {
|
|||
|
sandbox.log(msg, 'error');
|
|||
|
},
|
|||
|
warn: function (msg) {
|
|||
|
sandbox.log(msg, 'warn');
|
|||
|
},
|
|||
|
debug: function (msg) {
|
|||
|
sandbox.log(msg, 'debug');
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
if (adapter.config.enableSetObject) {
|
|||
|
sandbox.setObject = function (id, obj, callback) {
|
|||
|
if (debug) {
|
|||
|
sandbox.log('setObject(id=' + id + ', obj=' + JSON.stringify(obj) + ') - ' + words._('was not executed, while debug mode is active'), 'warn');
|
|||
|
if (typeof callback === 'function') {
|
|||
|
setTimeout(function () {
|
|||
|
try {
|
|||
|
callback.call(sandbox);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}, 0);
|
|||
|
}
|
|||
|
} else {
|
|||
|
if (sandbox.verbose) sandbox.log('setObject(id=' + id + ', obj=' + JSON.stringify(obj) + ')', 'info');
|
|||
|
adapter.setForeignObject(id, obj, callback);
|
|||
|
}
|
|||
|
};
|
|||
|
sandbox.extendObject = function (id, obj, callback) {
|
|||
|
if (debug) {
|
|||
|
sandbox.log('extendObject(id=' + id + ', obj=' + JSON.stringify(obj) + ') - ' + words._('was not executed, while debug mode is active'), 'warn');
|
|||
|
if (typeof callback === 'function') {
|
|||
|
setTimeout(function () {
|
|||
|
try {
|
|||
|
callback.call(sandbox);
|
|||
|
} catch (e) {
|
|||
|
errorInCallback(e); //adapter.log.error('Error in callback: ' + e)
|
|||
|
}
|
|||
|
}, 0);
|
|||
|
}
|
|||
|
} else {
|
|||
|
if (sandbox.verbose) sandbox.log('extendObject(id=' + id + ', obj=' + JSON.stringify(obj) + ')', 'info');
|
|||
|
adapter.extendForeignObject(id, obj, callback);
|
|||
|
}
|
|||
|
};
|
|||
|
}
|
|||
|
|
|||
|
// Make all predefined properties and methods readonly so scripts cannot overwrite them
|
|||
|
for (const prop in sandbox) {
|
|||
|
if (sandbox.hasOwnProperty(prop)) {
|
|||
|
Object.defineProperty(sandbox, prop, {
|
|||
|
configurable: false,
|
|||
|
writable: false
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return sandbox;
|
|||
|
}
|
|||
|
|
|||
|
module.exports = sandBox;
|