Socket
Socket
Sign inDemoInstall

node-red-contrib-sun-position

Package Overview
Dependencies
Maintainers
1
Versions
136
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

node-red-contrib-sun-position - npm Package Compare versions

Comparing version 1.1.7 to 1.2.0

examples/blind-control/wiki-1 open on civilDawn, not before 6 and close on civilDusk, not later than 23.json

34

CHANGELOG.md
# node-red-contrib-sun-position
#### 1.2.0: rework
- general
- changed several links to documentation #151
- replaced all remaining png type graphics with svg
- added examples
- blind-control + clock-time
- Standardization and consolidation of the same and similar functions #186
- minor bugs fixed with it
- time inject
- fixed bug, that interval between times does not start when load node and time in in the interval #189
- smaller changes which leads into more options for outgoing data
- time-comp
- redesign of the output, allows similar to the inject node set multiple properties
- so the node becomes like a kind of a change node
- time-span
- TODO: similar as time-comp
- within-time-switch
- allows to have setup the time limitation also by msg, flow, global or env variable. #192
#### 1.1.8: small enhancement
- time inject
- allow to have a given number of timer events between two times #188
- small enhancement in node status, shows interval if defined
- blind-control + clock-time
- allow to change general node settings with incoming message #184
#### 1.1.7: BugFix

@@ -4,0 +38,0 @@

608

nodes/clock-timer.js

@@ -7,11 +7,8 @@ /********************************************

const hlp = require(path.join(__dirname, '/lib/dateTimeHelper.js'));
const ctrlLib = require(path.join(__dirname, '/lib/timeControlHelper.js'));
const util = require('util');
const cRuleNoTime = -1;
const cRuleUntil = 0;
const cRuleFrom = 1;
// const cRuleAbsolute = 0;
const cRuleNone = 0;
const cRuleLogOperatorAnd = 2;
const cRuleLogOperatorOr = 1;

@@ -21,152 +18,7 @@ /******************************************************************************************/

'use strict';
/**
* evaluate temporary Data
* @param {*} node node Data
* @param {string} type type of type input
* @param {string} value value of typeinput
* @param {*} data data to cache
* @returns {*} data which was cached
*/
function evalTempData(node, type, value, data, tempData) {
// node.debug(`evalTempData type=${type} value=${value} data=${data}`);
const name = `${type}.${value}`;
if (data === null || typeof data === 'undefined') {
if (typeof tempData[name] !== 'undefined') {
if (type !== 'PlT') {
node.log(RED._('clock-timer.errors.usingTempValue', { type, value, usedValue: tempData[name] }));
}
return tempData[name];
}
if (node.nowarn[name]) {
return undefined; // only one error per run
}
node.warn(RED._('clock-timer.errors.warning', { message: RED._('clock-timer.errors.notEvaluableProperty', { type, value, usedValue: 'undefined' }) }));
node.nowarn[name] = true;
return undefined;
}
tempData[name] = data;
return data;
}
/******************************************************************************************/
/**
* reset any existing override
* @param {*} node node data
*/
function posOverwriteReset(node) {
node.debug(`posOverwriteReset expire=${node.nodeData.overwrite.expireTs}`);
node.nodeData.overwrite.active = false;
node.nodeData.overwrite.importance = 0;
if (node.timeOutObj) {
clearTimeout(node.timeOutObj);
node.timeOutObj = null;
}
if (node.nodeData.overwrite.expireTs || node.nodeData.overwrite.expires) {
delete node.nodeData.overwrite.expires;
delete node.nodeData.overwrite.expireTs;
delete node.nodeData.overwrite.expireDate;
delete node.nodeData.overwrite.expireDateISO;
delete node.nodeData.overwrite.expireDateUTC;
delete node.nodeData.overwrite.expireTimeLocal;
delete node.nodeData.overwrite.expireDateLocal;
}
}
/**
* setup the expiring of n override or update an existing expiring
* @param {*} node node data
* @param {Date} dNow the current timestamp
* @param {number} dExpire the expiring time, (if it is NaN, default time will be tried to use) if it is not used, nor a Number or less than 1 no expiring activated
*/
function setExpiringOverwrite(node, dNow, dExpire, reason) {
node.debug(`setExpiringOverwrite dNow=${dNow}, dExpire=${dExpire}, reason=${reason}`);
if (node.timeOutObj) {
clearTimeout(node.timeOutObj);
node.timeOutObj = null;
}
if (isNaN(dExpire)) {
dExpire = node.nodeData.overwrite.expireDuration;
node.debug(`using default expire value=${dExpire}`);
}
node.nodeData.overwrite.expires = Number.isFinite(dExpire) && (dExpire > 0);
if (!node.nodeData.overwrite.expires) {
node.log(`Overwrite is set which never expire (${reason})`);
node.debug(`expireNever expire=${dExpire}ms ${ typeof dExpire } - isNaN=${ isNaN(dExpire) } - finite=${ !isFinite(dExpire) } - min=${ dExpire < 100}`);
delete node.nodeData.overwrite.expireTs;
delete node.nodeData.overwrite.expireDate;
return;
}
node.nodeData.overwrite.expireTs = (dNow.getTime() + dExpire);
node.nodeData.overwrite.expireDate = new Date(node.nodeData.overwrite.expireTs);
node.nodeData.overwrite.expireDateISO = node.nodeData.overwrite.expireDate.toISOString();
node.nodeData.overwrite.expireDateUTC = node.nodeData.overwrite.expireDate.toUTCString();
node.nodeData.overwrite.expireDateLocal = node.positionConfig.toDateString(node.nodeData.overwrite.expireDate);
node.nodeData.overwrite.expireTimeLocal = node.positionConfig.toTimeString(node.nodeData.overwrite.expireDate);
node.log(`Overwrite is set which expires in ${dExpire}ms = ${node.nodeData.overwrite.expireDateISO} (${reason})`);
node.timeOutObj = setTimeout(() => {
node.log(`Overwrite is expired (timeout)`);
posOverwriteReset(node);
node.emit('input', { payload: -1, topic: 'internal-triggerOnly-overwriteExpired', force: false });
}, dExpire);
}
/**
* check if an override can be reset
* @param {*} node node data
* @param {*} msg message object
* @param {*} dNow current timestamp
*/
function checkOverrideReset(node, msg, dNow, isSignificant) {
if (node.nodeData.overwrite &&
node.nodeData.overwrite.expires &&
(node.nodeData.overwrite.expireTs < dNow.getTime())) {
node.log(`Overwrite is expired (trigger)`);
posOverwriteReset(node);
}
if (isSignificant) {
hlp.getMsgBoolValue(msg, ['reset','resetOverwrite'], 'resetOverwrite',
val => {
node.debug(`reset val="${util.inspect(val, { colors: true, compact: 10, breakLength: Infinity }) }"`);
if (val) {
if (node.nodeData.overwrite && node.nodeData.overwrite.active) {
node.log(`Overwrite reset by incoming message`);
}
posOverwriteReset(node);
}
});
}
}
/**
* setting the reason for override
* @param {*} node node data
*/
function setOverwriteReason(node) {
if (node.nodeData.overwrite.active) {
if (node.nodeData.overwrite.expireTs) {
node.reason.code = 3;
const obj = {
importance: node.nodeData.overwrite.importance,
timeLocal: node.nodeData.overwrite.expireTimeLocal,
dateLocal: node.nodeData.overwrite.expireDateLocal,
dateISO: node.nodeData.overwrite.expireDateISO,
dateUTC: node.nodeData.overwrite.expireDateUTC
};
node.reason.state = RED._('clock-timer.states.overwriteExpire', obj);
node.reason.description = RED._('clock-timer.reasons.overwriteExpire', obj);
} else {
node.reason.code = 2;
node.reason.state = RED._('clock-timer.states.overwriteNoExpire', { importance: node.nodeData.overwrite.importance });
node.reason.description = RED._('clock-timer.states.overwriteNoExpire', { importance: node.nodeData.overwrite.importance });
}
// node.debug(`overwrite exit true node.nodeData.overwrite.active=${node.nodeData.overwrite.active}`);
return true;
}
// node.debug(`overwrite exit true node.nodeData.overwrite.active=${node.nodeData.overwrite.active}`);
return false;
}
/**
* check if a manual overwrite should be set

@@ -187,6 +39,6 @@ * @param {*} node node data

}
checkOverrideReset(node, msg, dNow, isSignificant);
ctrlLib.checkOverrideReset(node, msg, dNow, isSignificant);
return p;
}, () => {
checkOverrideReset(node, msg, dNow, true);
ctrlLib.checkOverrideReset(node, msg, dNow, true);
return 0;

@@ -200,9 +52,11 @@ });

node.debug(`do not check any overwrite, importance of message ${nImportance} not matches current overwrite importance ${node.nodeData.overwrite.importance}`);
return setOverwriteReason(node);
return ctrlLib.setOverwriteReason(node);
}
const onlyTrigger = hlp.getMsgBoolValue(msg, ['trigger', 'noOverwrite'], ['triggerOnly', 'noOverwrite']);
if (onlyTrigger) {
return ctrlLib.setOverwriteReason(node);
}
let overrideData = undefined;
let overrideTopic = undefined;
if (!onlyTrigger && typeof msg.payload !== 'undefined') {
if (typeof msg.payload !== 'undefined') {
if (msg.topic && (msg.topic.includes('manual') ||

@@ -222,16 +76,3 @@ msg.topic.includes('overwrite'))) {

}
if ((typeof overrideData === 'undefined') && node.nodeData.overwrite.active) {
node.debug(`overwrite active, check of importance=${nImportance} or nExpire=${nExpire}`);
if (Number.isFinite(nExpire)) {
node.debug(`set to new expiring time nExpire="${nExpire}"`);
// set to new expiring time
setExpiringOverwrite(node, dNow, nExpire, 'set new expiring time by message');
}
if (nImportance > 0) {
// set to new importance
node.nodeData.overwrite.importance = nImportance;
}
// node.debug(`overwrite exit true node.nodeData.overwrite.active=${node.nodeData.overwrite.active}, expire=${nExpire}`);
return setOverwriteReason(node);
} else if (typeof overrideData !== 'undefined') {
if (typeof overrideData !== 'undefined') {
node.debug(`needOverwrite importance=${nImportance} expire=${nExpire}`);

@@ -247,3 +88,3 @@ if (typeof overrideData !== 'undefined') {

node.debug(`set expiring - expire is explizit defined "${nExpire}"`);
setExpiringOverwrite(node, dNow, nExpire, 'set expiring time by message');
ctrlLib.setExpiringOverwrite(node, dNow, nExpire, 'set expiring time by message');
} else if ((!exactImportance && (node.nodeData.overwrite.importance < nImportance)) || (!node.nodeData.overwrite.expireTs)) {

@@ -253,3 +94,3 @@ // isSignificant

node.debug(`no expire defined, using default or will not expire`);
setExpiringOverwrite(node, dNow, NaN, 'no special expire defined');
ctrlLib.setExpiringOverwrite(node, dNow, NaN, 'no special expire defined');
}

@@ -260,146 +101,21 @@ if (nImportance > 0) {

node.nodeData.overwrite.active = true;
} else if (node.nodeData.overwrite.active) {
node.debug(`overwrite active, check of nImportance=${nImportance} or nExpire=${nExpire}`);
if (Number.isFinite(nExpire)) {
node.debug(`set to new expiring time nExpire="${nExpire}"`);
// set to new expiring time
ctrlLib.setExpiringOverwrite(node, dNow, nExpire, 'set new expiring time by message');
}
if (nImportance > 0) {
// set to new importance
node.nodeData.overwrite.importance = nImportance;
}
}
// node.debug(`overwrite exit false node.nodeData.overwrite.active=${node.nodeData.overwrite.active}`);
return setOverwriteReason(node);
// node.debug(`overwrite exit node.nodeData.overwrite.active=${node.nodeData.overwrite.active}; expire=${nExpire}`);
return ctrlLib.setOverwriteReason(node);
}
/******************************************************************************************/
/**
* pre-checking conditions to may be able to store temp data
* @param {*} node node data
* @param {*} msg the message object
* @param {*} tempData the temporary storage object
*/
function prepareRules(node, msg, tempData) {
for (let i = 0; i < node.rules.count; ++i) {
const rule = node.rules.data[i];
if (rule.conditional) {
rule.conditon = {
result : false
};
for (let i = 0; i < rule.conditonData.length; i++) {
const el = rule.conditonData[i];
if (rule.conditon.result === true && el.condition.value === cRuleLogOperatorOr) {
break; // not nessesary, becaue already tue
} else if (rule.conditon.result === false && el.condition.value === cRuleLogOperatorAnd) {
break; // should never bekome true
}
delete el.operandValue;
delete el.thresholdValue;
el.result = node.positionConfig.comparePropValue(node, msg,
{
value: el.operand.value,
type: el.operand.type,
callback: (result, _obj) => { // opCallback
el.operandValue = _obj.value;
return evalTempData(node, _obj.type, _obj.value, result, tempData);
}
},
el.operator.value,
{
value: el.threshold.value,
type: el.threshold.type,
callback: (result, _obj) => { // opCallback
el.thresholdValue = _obj.value;
return evalTempData(node, _obj.type, _obj.value, result, tempData);
}
}
);
rule.conditon = {
index : i,
result : el.result,
text : el.text,
textShort : el.textShort
};
if (typeof el.thresholdValue !== 'undefined') {
rule.conditon.text += ' ' + el.thresholdValue;
rule.conditon.textShort += ' ' + hlp.clipStrLength(el.thresholdValue, 10);
}
}
}
}
}
/**
* get time constrainty of a rule
* @param {*} node node data
* @param {*} msg the message object
* @param {*} rule the rule data
* @return {number} timestamp of the rule
*/
function getRuleTimeData(node, msg, rule, dNow) {
rule.timeData = node.positionConfig.getTimeProp(node, msg, {
type: rule.timeType,
value : rule.timeValue,
offsetType : rule.offsetType,
offset : rule.offsetValue,
multiplier : rule.multiplier,
next : false,
dNow
});
if (rule.timeData.error) {
hlp.handleError(node, RED._('clock-timer.errors.error-time', { message: rule.timeData.error }), undefined, rule.timeData.error);
return -1;
} else if (!rule.timeData.value) {
throw new Error('Error can not calc time!');
}
rule.timeData.source = 'Default';
rule.timeData.ts = rule.timeData.value.getTime();
rule.timeData.dayId = hlp.getDayId(rule.timeData.value);
if (rule.timeMinType !== 'none') {
rule.timeDataMin = node.positionConfig.getTimeProp(node, msg, {
type: rule.timeMinType,
value: rule.timeMinValue,
offsetType: rule.offsetMinType,
offset: rule.offsetMinValue,
multiplier: rule.multiplierMin,
next: false,
dNow
});
const numMin = rule.timeDataMin.value.getTime();
rule.timeDataMin.source = 'Min';
if (rule.timeDataMin.error) {
hlp.handleError(node, RED._('clock-timer.errors.error-time', { message: rule.timeDataMin.error }), undefined, rule.timeDataAlt.error);
} else if (!rule.timeDataMin.value) {
throw new Error('Error can not calc Alt time!');
} else {
if (numMin > rule.timeData.ts) {
const tmp = rule.timeData;
rule.timeData = rule.timeDataMin;
rule.timeDataMin = tmp;
rule.timeData.ts = numMin;
rule.timeData.dayId = hlp.getDayId(rule.timeDataMin.value);
}
}
}
if (rule.timeMaxType !== 'none') {
rule.timeDataMax = node.positionConfig.getTimeProp(node, msg, {
type: rule.timeMaxType,
value: rule.timeMaxValue,
offsetType: rule.offsetMaxType,
offset: rule.offsetMaxValue,
multiplier: rule.multiplierMax,
next: false,
dNow
});
const numMax = rule.timeDataMax.value.getTime();
rule.timeDataMax.source = 'Max';
if (rule.timeDataMax.error) {
hlp.handleError(node, RED._('clock-timer.errors.error-time', { message: rule.timeDataMax.error }), undefined, rule.timeDataAlt.error);
} else if (!rule.timeDataMax.value) {
throw new Error('Error can not calc Alt time!');
} else {
if (numMax < rule.timeData.ts) {
const tmp = rule.timeData;
rule.timeData = rule.timeDataMax;
rule.timeDataMax = tmp;
rule.timeData.ts = numMax;
rule.timeData.dayId = hlp.getDayId(rule.timeDataMax.value);
}
}
}
return rule.timeData.ts;
}
/**
* check all rules and determinate the active rule

@@ -420,3 +136,3 @@ * @param {Object} node node data

const dayId = hlp.getDayId(dNow);
prepareRules(node, msg, tempData);
ctrlLib.prepareRules(node, msg, tempData);
// node.debug(`checkRules dNow=${dNow.toISOString()}, nowNr=${nowNr}, dayNr=${dayNr}, dateNr=${dateNr}, monthNr=${monthNr}, dayId=${dayId}, rules.count=${node.rules.count}, rules.lastUntil=${node.rules.lastUntil}`);

@@ -446,3 +162,3 @@

} catch (err) {
node.warn(RED._('clock-timer.errors.getPropertyData', err));
node.warn(RED._('node-red-contrib-sun-position/position-config:errors.getPropertyData', err));
node.debug(util.inspect(err, Object.getOwnPropertyNames(err)));

@@ -482,3 +198,3 @@ return null;

}
const num = getRuleTimeData(node, msg, rule, dNow);
const num = ctrlLib.getRuleTimeData(node, msg, rule, dNow);
// node.debug(`pos=${rule.pos} type=${rule.timeOpText} - ${rule.timeValue} - num=${num} - rule.timeData = ${ util.inspect(rule.timeData, { colors: true, compact: 40, breakLength: Infinity }) }`);

@@ -534,3 +250,3 @@ if (dayId === rule.timeData.dayId && num >=0 && (cmp(num) === true)) {

}
const num = getRuleTimeData(node, msg, rule, dNow);
const num = ctrlLib.getRuleTimeData(node, msg, rule, dNow);
if (num > nowNr) {

@@ -607,5 +323,4 @@ node.debug('autoTrigger set to rule ' + rule.pos);

}
livingRuleData.state= RED._('clock-timer.states.'+name, data);
livingRuleData.description = RED._('clock-timer.reasons.'+name, data);
// node.debug(`checkRules data=${util.inspect(data, { colors: true, compact: 10, breakLength: Infinity })}`);
livingRuleData.state= RED._('node-red-contrib-sun-position/position-config:ruleCtrl.states.'+name, data);
livingRuleData.description = RED._('node-red-contrib-sun-position/position-config:ruleCtrl.reasons.'+name, data);
// node.debug(`checkRules end livingRuleData=${util.inspect(livingRuleData, { colors: true, compact: 10, breakLength: Infinity })}`);

@@ -629,4 +344,4 @@ return livingRuleData;

livingRuleData.code = 1;
livingRuleData.state = RED._('clock-timer.states.default');
livingRuleData.description = RED._('clock-timer.reasons.default');
livingRuleData.state = RED._('node-red-contrib-sun-position/position-config:ruleCtrl.states.default');
livingRuleData.description = RED._('node-red-contrib-sun-position/position-config:ruleCtrl.reasons.default');

@@ -655,11 +370,12 @@ if (node.autoTrigger && node.rules && node.rules.count > 0) {

this.outputs = Number(config.outputs || 1);
const node = this;
if (config.autoTrigger) {
this.autoTrigger = {
node.autoTrigger = {
defaultTime : config.autoTriggerTime || 20 * 60000 // 20min
};
this.autoTriggerObj = null;
node.autoTriggerObj = null;
}
const node = this;
node.nowarn = {};

@@ -753,2 +469,27 @@ node.reason = {

}
// allow to overwrite settings by incomming message
if (msg.topic && (typeof msg.topic === 'string') && msg.topic.startsWith('set')) {
switch (msg.topic) {
case 'setAutoTriggerTime':
node.autoTrigger.defaultTime = parseFloat(msg.Payload) || node.autoTrigger.defaultTime;
break;
case 'setStoreName':
node.storeName = msg.Payload || node.storeName;
break;
case 'setSettingsTopic':
node.nodeData.topic = msg.Payload || node.nodeData.topic;
break;
default:
break;
}
if (node.nodeData.levelTop < node.nodeData.levelBottom) {
const tmp = node.nodeData.levelBottom;
node.nodeData.levelBottom = node.nodeData.levelTop;
node.nodeData.levelTop = tmp;
node.levelReverse = true;
}
}
// initialize
node.nowarn = {};

@@ -774,8 +515,7 @@ const tempData = node.context().get('cacheData',node.storeName) || {};

// check if the message contains any oversteering data
let ruleId = -2;
const timeCtrl = {
autoTrigger : node.autoTrigger
};
let ruleId = -2;
// check for manual overwrite

@@ -788,3 +528,3 @@ let overwrite = checkPosOverwrite(node, msg, dNow);

if (overwrite && timeCtrl.rule.resetOverwrite && timeCtrl.rule.id !== node.previousData.usedRule) {
posOverwriteReset(node);
ctrlLib.posOverwriteReset(node);
overwrite = false;

@@ -809,7 +549,7 @@ }

node.reason.code = NaN;
node.reason.state = RED._('clock-timer.states.startDelay', {date:node.positionConfig.toTimeString(node.startDelayTimeOut)});
node.reason.description = RED._('clock-timer.reasons.startDelay', {dateISO:node.startDelayTimeOut.toISOString()});
node.reason.state = RED._('node-red-contrib-sun-position/position-config:ruleCtrl.states.startDelay', {date:node.positionConfig.toTimeString(node.startDelayTimeOut)});
node.reason.description = RED._('node-red-contrib-sun-position/position-config:ruleCtrl.reasons.startDelay', {dateISO:node.startDelayTimeOut.toISOString()});
}
node.setState(node.payload.current);
let topic = node.payload.topic;
let topic = node.payload.topic || msg.topic;
if (topic) {

@@ -821,3 +561,3 @@ const topicAttrs = {

rule: ruleId,
newtopic: node.payload.topic,
newtopic: topic,
topic: msg.topic,

@@ -887,216 +627,4 @@ payload: msg.payload

// ####################################################################################################
/**
* initializes the node
*/
function initialize() {
node.debug('initialize ' + node.name + ' [' + node.id + ']');
const getName = (type, value) => {
if (type === 'num') {
return value;
} else if (type === 'str') {
return '"' + value + '"';
} else if (type === 'bool') {
return '"' + value + '"';
} else if (type === 'global' || type === 'flow') {
value = value.replace(/^#:(.+)::/, '');
}
return type + '.' + value;
};
const getNameShort = (type, value) => {
if (type === 'num') {
return value;
} else if (type === 'str') {
return '"' + hlp.clipStrLength(value,20) + '"';
} else if (type === 'bool') {
return '"' + value + '"';
} else if (type === 'global' || type === 'flow') {
value = value.replace(/^#:(.+)::/, '');
// special for Homematic Devices
if (/^.+\[('|").{18,}('|")\].*$/.test(value)) {
value = value.replace(/^.+\[('|")/, '').replace(/('|")\].*$/, '');
if (value.length > 25) {
return '...' + value.slice(-22);
}
return value;
}
}
if ((type + value).length > 25) {
return type + '...' + value.slice(-22);
}
return type + '.' + value;
};
// Prepare Rules
node.rules.count = node.rules.data.length;
node.rules.lastUntil = node.rules.count -1;
node.rules.firstFrom = node.rules.lastUntil;
node.rules.firstTimeLimited = node.rules.count;
node.rules.maxImportance = 0;
node.rules.canResetOverwrite = false;
for (let i = 0; i < node.rules.count; ++i) {
const rule = node.rules.data[i];
rule.pos = i + 1;
rule.name = rule.name || 'rule ' + rule.pos;
rule.resetOverwrite = (rule.resetOverwrite === true || rule.resetOverwrite === 'true') ? true : false;
rule.importance = Number(rule.importance) || 0;
node.rules.maxImportance = Math.max(node.rules.maxImportance, rule.importance);
node.rules.canResetOverwrite = node.rules.canResetOverwrite || rule.resetOverwrite;
rule.timeOp = Number(rule.timeOp) || cRuleUntil;
rule.timeLimited = (rule.timeType && (rule.timeType !== 'none'));
if (!rule.timeLimited) {
rule.timeOp = cRuleNoTime;
delete rule.offsetType;
delete rule.multiplier;
delete rule.timeMinType;
delete rule.timeMinValue;
delete rule.offsetMinType;
delete rule.multiplierMin;
delete rule.timeMaxType;
delete rule.timeMaxValue;
delete rule.offsetMaxType;
delete rule.multiplierMax;
delete rule.timeDays;
delete rule.timeMonths;
delete rule.timeOnlyOddDays;
delete rule.timeOnlyEvenDays;
delete rule.timeDateStart;
delete rule.timeDateEnd;
} else {
rule.offsetType = rule.offsetType || 'none';
rule.multiplier = rule.multiplier || 60000;
rule.timeMinType = rule.timeMinType || 'none';
rule.timeMinValue = (rule.timeMinValue || '');
rule.offsetMinType = rule.offsetMinType || 'none';
rule.multiplierMin = rule.multiplierMin || 60000;
rule.timeMaxType = rule.timeMaxType || 'none';
rule.timeMaxValue = (rule.timeMaxValue || '');
rule.offsetMaxType = rule.offsetMaxType || 'none';
rule.multiplierMax = rule.multiplierMax || 60000;
node.rules.firstTimeLimited = Math.min(i,node.rules.firstTimeLimited);
if (rule.timeOp === cRuleUntil) {
node.rules.lastUntil = i;
}
if (rule.timeOp === cRuleFrom) {
node.rules.firstFrom = Math.min(i,node.rules.firstFrom);
}
if (!rule.timeDays || rule.timeDays === '*') {
rule.timeDays = null;
} else {
rule.timeDays = rule.timeDays.split(',');
rule.timeDays = rule.timeDays.map( e => parseInt(e) );
}
if (!rule.timeMonths || rule.timeMonths === '*') {
rule.timeMonths = null;
} else {
rule.timeMonths = rule.timeMonths.split(',');
rule.timeMonths = rule.timeMonths.map( e => parseInt(e) );
}
if (rule.timeOnlyOddDays && rule.timeOnlyEvenDays) {
rule.timeOnlyOddDays = false;
rule.timeOnlyEvenDays = false;
}
rule.timeDateStart = rule.timeDateStart || '';
rule.timeDateEnd = rule.timeDateEnd || '';
if (rule.timeDateStart || rule.timeDateEnd) {
if (rule.timeDateStart) {
rule.timeDateStart = new Date(rule.timeDateStart);
rule.timeDateStart.setHours(0, 0, 0, 1);
} else {
rule.timeDateStart = new Date(2000,0,1,0, 0, 0, 1);
}
if (rule.timeDateEnd) {
rule.timeDateEnd = new Date(rule.timeDateEnd);
rule.timeDateEnd.setHours(23, 59, 59, 999);
} else {
rule.timeDateEnd = new Date(2000,11,31, 23, 59, 59, 999);
}
}
}
rule.conditonData = [];
const setCondObj = (pretext, defLgOp) => {
const operandAType = rule[pretext+'OperandAType'];
const conditionValue = Number(rule[pretext+'LogOperator']) || defLgOp;
if (operandAType !== 'none' && conditionValue !== cRuleNone) {
const operandAValue = rule[pretext+'OperandAValue'];
const operandBType = rule[pretext+'OperandBType'];
const operandBValue = rule[pretext+'OperandBValue'];
const el = {
result: false,
operandName: getName(operandAType, operandAValue),
thresholdName: getName(operandBType, operandBValue),
operand: {
type:operandAType,
value:operandAValue
},
threshold: {
type:operandBType,
value:operandBValue
},
operator: {
value : rule[pretext+'Operator'],
text : rule[pretext+'OperatorText'],
description: RED._('node-red-contrib-sun-position/position-config:common.comparatorDescription.' + rule[pretext+'Operator'])
},
condition: {
value : conditionValue,
text : rule[pretext+'LogOperatorText']
}
};
if (el.operandName.length > 25) {
el.operandNameShort = getNameShort(operandAType, operandAValue);
}
if (el.thresholdName.length > 25) {
el.thresholdNameShort = getNameShort(operandBType, operandBValue);
}
el.text = el.operandName + ' ' + el.operator.text;
el.textShort = (el.operandNameShort || el.operandName) + ' ' + el.operator.text;
rule.conditonData.push(el);
}
delete rule[pretext+'OperandAType'];
delete rule[pretext+'OperandAValue'];
delete rule[pretext+'OperandBType'];
delete rule[pretext+'OperandBValue'];
delete rule[pretext+'Operator'];
delete rule[pretext+'OperatorText'];
delete rule[pretext+'LogOperator'];
delete rule[pretext+'LogOperatorText'];
};
setCondObj('valid', cRuleLogOperatorOr);
setCondObj('valid2', cRuleNone);
rule.conditional = rule.conditonData.length > 0;
}
if (node.autoTrigger || (parseFloat(config.startDelayTime) > 9)) {
let delay = parseFloat(config.startDelayTime) || (2000 + Math.floor(Math.random() * 8000)); // 2s - 10s
delay = Math.min(delay, 2147483646);
node.startDelayTimeOut = new Date(Date.now() + delay);
setTimeout(() => {
delete node.startDelayTimeOut;
node.emit('input', {
topic: 'autoTrigger/triggerOnly/start',
payload: 'triggerOnly',
triggerOnly: true
});
}, delay);
}
}
try {
initialize();
ctrlLib.initializeCtrl(RED, node, config);
} catch (err) {

@@ -1103,0 +631,0 @@ node.error(err.message);

@@ -163,3 +163,3 @@ {

"tips": {
"sunPosControl": "<a href=\"https://github.com/rdmtc/node-red-contrib-sun-position/blob/HEAD/blind_control.md\">Dokumentation und Beispiele</a>"
"documentation": "<a href=\"https://github.com/rdmtc/node-red-contrib-sun-position/wiki/blind-control\">Dokumentation und Beispiele</a>"
},

@@ -166,0 +166,0 @@ "reasons": {

@@ -94,3 +94,3 @@ {

"tips": {
"timerControl": "<a href=\"https://github.com/rdmtc/node-red-contrib-sun-position/blob/HEAD/clock_timer.md\">Dokumentation und Beispiele</a>"
"documentation": "<a href=\"https://github.com/rdmtc/node-red-contrib-sun-position/wiki/clock-timer\">Dokumentation und Beispiele</a>"
},

@@ -97,0 +97,0 @@ "reasons": {

@@ -9,5 +9,5 @@ {

"tips": {
"moonPosControl" : "If a message arrives through the Input the calculated position of the moon will be always send to the first output. If additionally specified upper and lower limits for the moon radiation and the azimuth is inside the defined limits the incoming message will send to the associated output."
"documentation" : "If a message arrives through the Input the calculated position of the moon will be always send to the first output. If additionally specified upper and lower limits for the moon radiation and the azimuth is inside the defined limits the incoming message will send to the associated output. <a href=\"https://github.com/rdmtc/node-red-contrib-sun-position/wiki/moon-position\">Dokumentation und Beispiele</a>"
}
}
}

@@ -13,3 +13,5 @@ {

"checkall" : "Alle Regeln überprüfen",
"stopfirst" : "Nach erster Übereinstimmung stoppen (if)"
"stopfirst" : "Nach erster Übereinstimmung stoppen (if)",
"nextOccurrence": "next occurrence",
"on": "nur"
},

@@ -16,0 +18,0 @@ "types": {

@@ -15,8 +15,4 @@ {

"endOffset": 0
},
"tips": {
"sunPosControl" : "If a message arrives through the Input the calculated position of the sun will be always send to the first output. If additionally specified upper and lower limits for the solar radiation and the azimuth is inside the defined limits the incoming message will send to the associated output.",
"config": "This configuration is used to add a sunInSky property of the message emitted to the first output. This can be used to work with node-red-contrib-blindcontroller nodes"
}
}
}

@@ -5,30 +5,13 @@ {

"property": "Property",
"propertyThreshold": "Schwelle",
"propertyThreshold": "Schwelle",
"time": "Zeit",
"offset": "Offset",
"timeStart": "Time start",
"timeEnd": "Time end",
"timeStartOffset": "Start Offset",
"timeEndOffset": "End Offset",
"timeAlt": "Alternative Zeit",
"timeAltoffset": "Offset",
"payloadOffset": "Offset",
"payloadFormat": "Format",
"addPayload1": "set",
"addPayload2": "(2) set",
"addPayload3": "(3) set",
"addPayload1Value": "Wert",
"addPayload2Value": "Wert",
"addPayload3Value": "Wert",
"addPayload1Offset": "Offset",
"addPayload2Offset": "Offset",
"addPayload3Offset": "Offset",
"addPayload1Next": "Zeitpunkt",
"addPayload2Next": "Zeitpunkt",
"addPayload3Next": "Zeitpunkt",
"addPayload1Next2": "nächstes Vorkommen",
"addPayload2Next2": "nächstes Vorkommen",
"addPayload3Next2": "nächstes Vorkommen",
"addPayload1Format": "Zeitformat",
"addPayload2Format": "Zeitformat",
"addPayload3Format": "Zeitformat",
"recalcTime": "Neuber.",
"hours": "Stunden",
"on": "nur",
"once": "zusätzlich",

@@ -39,2 +22,3 @@ "onstart": "Inject bei start ",

"validForMonths":"gültig für Monate",
"validForDates":"Gültigkeitsdauer (Jahr wird ignoriert)",
"specialDays":"spezielle Tage",

@@ -46,3 +30,5 @@ "onlyEvenDays":"nur gerade",

"between":"zwischen",
"and":"und"
"and":"und",
"interval-amount":"feste Anzahl zwischen den Zeitpunkten",
"count":"Anzahl"
},

@@ -63,7 +49,2 @@ "placeholder": {

"months":"Monate auswählen, an denen die nachricht gesendet werden soll",
"addPayload": "zusätzliche Eigenschaften der gesendeten nachricht festlegen",
"addPayloadValue": "Wert der zusätzlichen Eigenschaft",
"addPayloadOffset": "Offset des Wertes",
"addPayloadNext": "markiert=nächstes auftreten; unmarkiert=aktueller Tag, auch wenn in der Vergangenheit",
"addPayloadFormat": "format des Wertes",
"recalcTime": "Intervall in dem eine Neuberechnung des Zeitpunktes zum Senden der Nachricht stattfinden soll.",

@@ -74,4 +55,3 @@ "once": "zusätzlich soll beim Start des Flows eine Nachricht (unabhängig von der Zeit) gesendet werden"

"addTimes": "Alternative Zeit: Wenn die angegebene Eigenschaft den Wert 'true' besitzt, wird anstatt zur normal angegebenen zeit zum angegebenem alternativen Zeitpunkt getriggert. Dies ist nützlich, um an Feiertagen oder anderen besonderen Tagen zu einem anderen Zeitpunkt einen Flow zu triggern.",
"recalc": "Wenn der Zeitstempel durch einen flow oder global context definiert ist, führen Änderungen an den Werten nicht unmittelbar zu einer Änderung der geplanten Trigger-Zeit, sondern immer nur wenn eine Neuberechnung durchgeführt wird. Hiermit kann eine reguläre Neuberechnung eingestellt werden.",
"addPayload": "Mit diesen zusätzlichen Eigenschaften können Sie bis zu 3 zusätzliche Werte zum Trigger Zeitpunkt in der Nachricht,im Flow oder globalen Kontext setzen. Das kann nützlich sein um beispielsweise einen extra Zeitstempel in Bezug auf den Zeitpunkt des Triggers der Nachricht zu definieren. So kann beispielsweise beim Einschalten gleich der Ausschaltzeitpunkt mitgegeben werden."
"recalc": "Wenn der Zeitstempel durch einen flow oder global context definiert ist, führen Änderungen an den Werten nicht unmittelbar zu einer Änderung der geplanten Trigger-Zeit, sondern immer nur wenn eine Neuberechnung durchgeführt wird. Hiermit kann eine reguläre Neuberechnung eingestellt werden."
},

@@ -78,0 +58,0 @@ "message": {

@@ -33,3 +33,5 @@ {

"onlyEvenDays":"nur gerade",
"onlyOddDays":"nur ungerade"
"onlyOddDays":"nur ungerade",
"timeRestrictions":"Zeit begrenzen",
"internally":"👇 ist hier definiert"
},

@@ -49,3 +51,4 @@ "placeholder": {

"endOffsetAlt": "0",
"timeLimits":"Wählen Sie Tage und / oder Monate aus, für welche es gültig sein soll"
"timeLimits":"Wählen Sie Tage und / oder Monate aus, für welche es gültig sein soll",
"timeRestrictions":"Objekt Pfad"
},

@@ -52,0 +55,0 @@ "tips": {

@@ -132,2 +132,3 @@ {

"autoTrigger": "automatic triggers new calculation of the blind position in a dynamic changing time interval",
"autoTriggerTime": "10",
"startDelay": "0 - off",

@@ -164,56 +165,8 @@ "startDelay2": "delay any output to the first (or only) output on start (Node-Red start, Deplay, ...). Set to 0 to deactivate.",

"tips": {
"sunPosControl": "<a href=\"https://github.com/rdmtc/node-red-contrib-sun-position/blob/HEAD/blind_control.md\">Documentation and examples</a>"
"documentation": "<a href=\"https://github.com/rdmtc/node-red-contrib-sun-position/wiki/blind-control\">Documentation and examples</a>"
},
"reasons": {
"overwriteNoExpire": "position is overwritten with importance __importance__",
"overwriteExpire": "position is overwritten with importance __importance__, will expire __dateISO__",
"default": "position is set to default position because no other rule matches",
"ruleTime": "position by time - rule __timeOp__ __timeLocal__ [__number__] __name__",
"ruleCond": "position by conditional rule __text__ [__number__] __name__",
"ruleTimeCond": "position by time rule __timeOp__ __timeLocal__ and condition __text__ [__number__] __name__",
"rule": "position by fixed rule [__number__] __name__",
"sunMinAltitude": "sun below minimum altitude",
"sunMinDelta": "change is less ad min delta",
"sunNotInWin": "Sun not in window",
"sunCtrl": "sun control",
"sunCtrlMin": "__org__ (__level__ is below minimum)",
"sunCtrlMax": "__org__ (__level__ is above maximum)",
"oversteer": "sun position oversteers",
"smooth": "Position is not changed to __pos__ because not enough time has passed since the last change (time smooth)",
"sunInWinMax": "Sun in window (Level is maximum)",
"sunNotInWinMin": "Sun not in window (Level is minimum)",
"ruleMin": "__org__ (__level__ is below minimum) [__number__] __name__",
"ruleMax": "__org__ (__level__ is above maximum) [__number__] __name__",
"startDelay": "node in startup until __dateISO__"
},
"states": {
"overwriteNoExpire": "overwritten [imp=__importance__]",
"overwriteExpire": "overwritten [imp=__importance__], till __timeLocal__",
"default": "default",
"ruleTime": "__timeOp__ __timeLocal__ [__number__] __name__",
"ruleCond": "__textShort__ [__number__] __name__",
"ruleTimeCond": "__timeOp__ __timeLocal__ + cond __operatorText__ [__number__] __name__",
"rule": "by fixed rule [__number__] __name__",
"sunMinAltitude": "min altitude",
"sunMinDelta": "min delta",
"sunNotInWin": "no sun in window",
"sunCtrl": "sun control",
"sunCtrlMin": "__org__ (min)",
"sunCtrlMax": "__org__ (max)",
"oversteer": "oversteer",
"smooth": "block change to __pos__",
"sunInWinMax": "Sun in window (max)",
"sunNotInWinMin": "Sun not in window (min)",
"ruleMin": "__org__ [min rule __number__]",
"ruleMax": "__org__ [max rule __number__]",
"startDelay": "startup [__date__]"
},
"errors": {
"warning": "Warning: __message__",
"internal": "Error: __message__",
"error-time": "Error get time: __message__",
"invalid-blind-level": "Given Blind-Position __pos__ is not a valid Position!",
"getOversteerData": "error getting oversteer data: __message__",
"getBlindPosData": "error getting blind level: __message__",
"getPropertyData": "error getting condition data: \"__message__\" skipping time",
"smoothTimeToolong": "The selected smooth is too long!!",

@@ -220,0 +173,0 @@ "usingTempValue": "Could not evaluate __type__.__value__ and using stored value \"__usedValue__\"!",

@@ -94,37 +94,5 @@ {

"tips": {
"timerControl": "<a href=\"https://github.com/rdmtc/node-red-contrib-sun-position/blob/HEAD/clock_timer.md\">Documentation and examples</a>"
},
"reasons": {
"overwriteNoExpire": "position is overwritten with importance __importance__",
"overwriteExpire": "position is overwritten with importance __importance__, will expire __dateISO__",
"default": "position is set to default position because no other rule matches",
"ruleTime": "position by time - rule __timeOp__ __timeLocal__ [__number__] __name__",
"ruleCond": "position by conditional rule __text__ [__number__] __name__",
"ruleTimeCond": "position by time rule __timeOp__ __timeLocal__ and condition __text__ [__number__] __name__",
"rule": "position by fixed rule [__number__] __name__",
"ruleMin": "__org__ (__level__ is below minimum) [__number__] __name__",
"ruleMax": "__org__ (__level__ is above maximum) [__number__] __name__",
"startDelay": "node in startup until __dateISO__"
},
"states": {
"overwriteNoExpire": "overwritten [imp=__importance__]",
"overwriteExpire": "overwritten [imp=__importance__], till __timeLocal__",
"default": "default",
"ruleTime": "__timeOp__ __timeLocal__ [__number__] __name__",
"ruleCond": "__textShort__ [__number__] __name__",
"ruleTimeCond": "__timeOp__ __timeLocal__ + cond __operatorText__ [__number__] __name__",
"rule": "by fixed rule [__number__] __name__",
"ruleMin": "__org__ [min rule __number__]",
"ruleMax": "__org__ [max rule __number__]",
"startDelay": "startup [__date__]"
},
"errors": {
"warning": "Warning: __message__",
"internal": "Error: __message__",
"error-time": "Error get time: __message__",
"getPropertyData": "error getting condition data: \"__message__\" skipping time",
"usingTempValue": "Could not evaluate __type__.__value__ and using stored value \"__usedValue__\"!",
"notEvaluableProperty": "could not evaluate __type__.__value__, using \"__usedValue__\"!"
"documentation": "<a href=\"https://github.com/rdmtc/node-red-contrib-sun-position/wiki/clock-timer\">Documentation and examples</a>"
}
}
}

@@ -9,5 +9,5 @@ {

"tips": {
"moonPosControl" : "If a message arrives through the Input the calculated position of the moon will be always send to the first output. If additionally specified upper and lower limits for the moon radiation and the azimuth is inside the defined limits the incoming message will send to the associated output."
"documentation" : "If a message arrives through the Input the calculated position of the moon will be always send to the first output. If additionally specified upper and lower limits for the moon radiation and the azimuth is inside the defined limits the incoming message will send to the associated output. <a href=\"https://github.com/rdmtc/node-red-contrib-sun-position/wiki/moon-position\">Documentation and examples</a>"
}
}
}

@@ -14,3 +14,11 @@ {

"checkall": "stopping after first match (if)",
"stopfirst": "checking all rules"
"stopfirst": "checking all rules",
"nextOccurrence": "next occurrence",
"on": "on",
"validForDays": "valid days",
"validForMonths": "valid months",
"validForDates": "Gültigkeitsdauer (Jahr wird ignoriert)",
"specialDays": "special days",
"onlyEvenDays":"only even",
"onlyOddDays":"only odd"
},

@@ -66,3 +74,3 @@ "types": {

"goldenHourDawnStart": "golden hour dawn start",
"sunrise": "sunrise",
"sunrise": "sunrise start",
"sunriseEnd": "sunrise end",

@@ -73,3 +81,3 @@ "goldenHourDawnEnd": "golden hour dawn end",

"sunsetStart": "sunset start",
"sunset": "sunset",
"sunset": "sunset end",
"goldenHourDuskEnd": "golden hour dusk end",

@@ -308,3 +316,50 @@ "blueHourDuskStart": "blue hour dusk start",

},
"ruleCtrl": {
"reasons": {
"overwriteNoExpire": "position is overwritten with importance __importance__",
"overwriteExpire": "position is overwritten with importance __importance__, will expire __dateISO__",
"default": "position is set to default position because no other rule matches",
"ruleTime": "position by time - rule __timeOp__ __timeLocal__ [__number__] __name__",
"ruleCond": "position by conditional rule __text__ [__number__] __name__",
"ruleTimeCond": "position by time rule __timeOp__ __timeLocal__ and condition __text__ [__number__] __name__",
"rule": "position by fixed rule [__number__] __name__",
"sunMinAltitude": "sun below minimum altitude",
"sunMinDelta": "change is less ad min delta",
"sunNotInWin": "Sun not in window",
"sunCtrl": "sun control",
"sunCtrlMin": "__org__ (__level__ is below minimum)",
"sunCtrlMax": "__org__ (__level__ is above maximum)",
"oversteer": "sun position oversteers",
"smooth": "Position is not changed to __pos__ because not enough time has passed since the last change (time smooth)",
"sunInWinMax": "Sun in window (Level is maximum)",
"sunNotInWinMin": "Sun not in window (Level is minimum)",
"ruleMin": "__org__ (__level__ is below minimum) [__number__] __name__",
"ruleMax": "__org__ (__level__ is above maximum) [__number__] __name__",
"startDelay": "node in startup until __dateISO__"
},
"states": {
"overwriteNoExpire": "overwritten [imp=__importance__]",
"overwriteExpire": "overwritten [imp=__importance__], till __timeLocal__",
"default": "default",
"ruleTime": "__timeOp__ __timeLocal__ [__number__] __name__",
"ruleCond": "__textShort__ [__number__] __name__",
"ruleTimeCond": "__timeOp__ __timeLocal__ + cond __operatorText__ [__number__] __name__",
"rule": "by fixed rule [__number__] __name__",
"sunMinAltitude": "min altitude",
"sunMinDelta": "min delta",
"sunNotInWin": "no sun in window",
"sunCtrl": "sun control",
"sunCtrlMin": "__org__ (min)",
"sunCtrlMax": "__org__ (max)",
"oversteer": "oversteer",
"smooth": "block change to __pos__",
"sunInWinMax": "Sun in window (max)",
"sunNotInWinMin": "Sun not in window (min)",
"ruleMin": "__org__ [min rule __number__]",
"ruleMax": "__org__ [max rule __number__]",
"startDelay": "startup [__date__]"
}
},
"errors": {
"warning": "Warning: __message__",
"error": "Error: __message__",

@@ -320,4 +375,7 @@ "error-title": "internal error",

"notEvaluablePropertyAdd":"Error \"__err__\", could not evaluate __type__.__value__",
"notEvaluablePropertyUsedValue": "could not evaluate __type__.__value__, using \"__usedValue__\"!",
"invalidParameter":"Parameter \"__param__\" is invalid \"__type__\" (using __newValue__)",
"invalid-expr": "Invalid JSONata expression: __error__"
"invalid-expr": "Invalid JSONata expression: __error__",
"getPropertyData": "error getting condition data: \"__message__\" skipping time",
"error-time": "Error get time: __message__"
},

@@ -324,0 +382,0 @@ "position-config": {

@@ -17,3 +17,3 @@ {

"tips": {
"sunPosControl" : "If a message arrives through the Input the calculated position of the sun will be always send to the first output. If additionally specified upper and lower limits for the solar radiation and the azimuth is inside the defined limits the incoming message will send to the associated output.",
"documentation" : "If a message arrives through the Input the calculated position of the sun will be always send to the first output. If additionally specified upper and lower limits for the solar radiation and the azimuth is inside the defined limits the incoming message will send to the associated output. <a href=\"https://github.com/rdmtc/node-red-contrib-sun-position/wiki/sun-position\">Documentation and examples</a>",
"config": "This configuration is used to add a sunInSky property of the message emitted to the first output. This can be used to work with node-red-contrib-blindcontroller nodes"

@@ -20,0 +20,0 @@ }

@@ -10,2 +10,3 @@ {

"operatorContainer": "compare with",
"resultContainer":"result",
"onlyif": "only if",

@@ -29,4 +30,7 @@ "result1": "result",

"default": ""
}
},
"tips": {
"documentation": "<a href=\"https://github.com/rdmtc/node-red-contrib-sun-position/wiki/time-comp\">Documentation and examples</a>"
}
}
}

@@ -7,2 +7,4 @@ {

"propertyThreshold": "Threshold",
"time": "Time",
"offset": "Offset",
"timeStart": "Time start",

@@ -12,16 +14,15 @@ "timeEnd": "Time end",

"timeEndOffset": "End Offset",
"nextOccurrence": "next occurrence",
"on":"on",
"timeAlt": "Alternate time",
"timeAltoffset": "Offset",
"recalcTime": "recalculation time",
"hours": "Hours",
"once": "additional",
"onstart": "Inject on start ",
"onceDelay": "seconds",
"validForDays":"valid days",
"validForMonths":"valid months",
"specialDays":"special days",
"onlyEvenDays":"only even",
"onlyOddDays":"only odd",
"fewDays": "few days",
"fewMonths": "few months",
"between":"between",
"and":"and"
"and":"and",
"interval-amount":"fixed number between time",
"count":"Count"
},

@@ -32,17 +33,21 @@ "placeholder": {

"topic": "Name",
"property": "Property",
"propertyThreshold": "Threshold",
"payload": "payload data of the send message",
"time": "time for inject",
"timeOffset": "offset of time",
"timeAlt": "alternate time",
"timeAltOffset": "offset of the alternate time",
"timeLimits":"Select days and / or months for which it should be valid",
"days":"select days wherefore it should be valid",
"months":"select months wherefore it should be valid",
"addPayload": "allow to add additional property to the send message",
"addPayloadValue": "value of the additional property",
"addPayloadOffset": "Offset to the value",
"addPayloadNext": "checked=next occurence, unchecked=current day even if in the past",
"addPayloadFormat": "format of the output",
"once": "define if the node should emit a message independent of the time a message on flow start"
"recalcTime": "Interval of a recalculation of the time for send a message.",
"once": "define if the node should emit a message independent of the time a message on flow start",
"start":"tt.mm",
"end":"tt.mm"
},
"tips": {
"addPayload": "With the additional message properties you can send the message with define up to 3 additional properties with enhanced settings. Especial there can be defined timestamps in relation to the message send timestamp."
"addTimes": "Alternative time: If the specified property has the value 'true', the trigger is triggered at the specified alternative time instead of the normal specified time. This is useful for triggering a flow at a different time on public holidays or other special days.",
"recalc": "If the time stamp is defined by a flow or global context, changes to the values do not lead to an immediate change in the planned trigger time, but only when a recalculation is carried out. This can be used to set a regular recalculation.",
"documentation": "<a href=\"https://github.com/rdmtc/node-red-contrib-sun-position/wiki/time-inject\">Documentation and examples</a>"
},

@@ -49,0 +54,0 @@ "message": {

@@ -30,4 +30,7 @@ {

"resultOffset": "Offset"
}
},
"tips": {
"documentation": "<a href=\"https://github.com/rdmtc/node-red-contrib-sun-position/wiki/time-span\">Documentation and examples</a>"
}
}
}

@@ -38,3 +38,5 @@ {

"onlyEvenDays":"only even",
"onlyOddDays":"only odd"
"onlyOddDays":"only odd",
"timeRestrictions":"limit time",
"internally":"👇 is defined here"
},

@@ -57,3 +59,4 @@ "placeholder": {

"tips": {
"addTimes": "Here can be defined alternate times for start/end. If the given property is true the alternate start/end times will be used instead the normal ones. This can be used to have different start/end times for holidays or other special days."
"addTimes": "Here can be defined alternate times for start/end. If the given property is true the alternate start/end times will be used instead the normal ones. This can be used to have different start/end times for holidays or other special days.",
"documentation": "<a href=\"https://github.com/rdmtc/node-red-contrib-sun-position/wiki/within-time\">Documentation and examples</a>"
},

@@ -60,0 +63,0 @@ "errors": {

@@ -29,26 +29,55 @@ /********************************************

this.result1 = {
type : config.result1Type,
value : config.result1,
format : config.result1Format
};
if (!Array.isArray(config.results)) {
config.results = [];
if (config.result1Type && config.result1Type !== 'none') {
config.results.push({
p: config.result1 ? config.result1 : 'msgPayload',
pt: config.result1Type ? config.result1Type : 'input',
v: config.result1Value ? config.result1Value : '',
vt: config.result1ValueType ? config.result1ValueType : 'input',
o: config.result1Offset ? config.result1Offset : 1,
oT: (config.result1OffsetType === 0 || config.result1OffsetType === '') ? 'none' : (config.result1OffsetType ? config.result1OffsetType : 'num'),
oM: config.result1OffsetMultiplier ? config.result1OffsetMultiplier : 60000,
f: config.result1Format ? config.result1Format : 0,
next: false,
days: '*'
});
}
this.result1Value = {
type: config.result1ValueType,
value: config.result1Value,
format: config.result1Format,
offsetType: config.result1OffsetType,
offset: config.result1Offset,
multiplier: config.result1Multiplier,
next: true
};
if (this.positionConfig && this.result1Value.type === 'jsonata') {
try {
this.result1Value.expr = this.positionConfig.getJSONataExpression(this, this.result1Value.value);
} catch (err) {
this.error(RED._('node-red-contrib-sun-position/position-config:errors.invalid-expr', { error:err.message }));
this.result1Value.expr = null;
}
delete config.result1;
delete config.result1Type;
delete config.result1Value;
delete config.result1ValueType;
delete config.result1Format;
delete config.result1Offset;
delete config.result1OffsetType;
delete config.result1OffsetMultiplier;
}
this.results = [];
config.results.forEach(prop => {
const propNew = {
outType: prop.pt,
outValue: prop.p,
type: prop.vt,
value: prop.v,
format: prop.f,
offsetType: prop.oT,
offset: prop.o,
multiplier: prop.oM,
next: (typeof prop.next === 'undefined' || prop.next === null || prop.next === true || prop.next === 'true') ? true : false,
days: prop.days
};
if (this.positionConfig && propNew.type === 'jsonata') {
try {
propNew.expr = this.positionConfig.getJSONataExpression(this, propNew.value);
} catch (err) {
this.error(RED._('node-red-contrib-sun-position/position-config:errors.invalid-expr', { error: err.message }));
propNew.expr = null;
}
}
this.results.push(propNew);
});
this.rules = config.rules;

@@ -62,2 +91,3 @@ this.checkall = config.checkall;

send = send || function (...args) { node.send.apply(node, args); };
const dNow = new Date();

@@ -83,17 +113,20 @@ if (node.positionConfig === null ||

if (node.result1.type !== 'none') {
for (let i = 0; i < node.results.length; i++) {
const prop = node.results[i];
// node.debug(`prepOutMsg-${i} node.results[${i}]=${util.inspect(prop, { colors: true, compact: 10, breakLength: Infinity })}`);
let resultObj = null;
if (node.result1Value.type === 'input') {
resultObj = hlp.getFormattedDateOut(inputData.value, node.result1.format);
if (prop.type === 'input') {
resultObj = node.positionConfig.formatOutDate(this, msg, inputData.value, prop);
} else {
resultObj = node.positionConfig.getOutDataProp(node, msg, node.result1Value);
resultObj = node.positionConfig.getOutDataProp(this, msg, prop, dNow);
}
if (resultObj === null || typeof resultObj === 'undefined') {
throw new Error('could not evaluate ' + node.result1Value.type + '.' + node.result1Value.value);
if (resultObj === null || (typeof resultObj === 'undefined')) {
this.error('Could not evaluate ' + prop.type + '.' + prop.value + '. - Maybe settings outdated (open and save again)!');
} else if (resultObj.error) {
node.error('error on getting result: ' + resultObj.error);
this.error('error on getting result: "' + resultObj.error + '"');
} else {
node.positionConfig.setMessageProp(this, msg, node.result1.type, node.result1.value, resultObj);
node.positionConfig.setMessageProp(this, msg, prop.outType, prop.outValue, resultObj);
}
// node.debug(`prepOutMsg-${i} msg=${util.inspect(msg, { colors: true, compact: 10, breakLength: Infinity })}`);
}

@@ -100,0 +133,0 @@

@@ -15,20 +15,2 @@ /********************************************

/**
* get the schedule time
* @param {Date} time - time to schedule
* @param {number} [limit] - minimal time limit to schedule
* @returns {number} milliseconds until the defined Date
*/
function tsGetScheduleTime(time, limit) {
const dNow = new Date();
let millisec = time.valueOf() - dNow.valueOf();
if (limit) {
while (millisec < limit) {
millisec += 86400000; // 24h
}
}
return millisec;
}
/**
* timeInjectNode

@@ -42,3 +24,4 @@ * @param {*} config - configuration

interval : 2,
intervalTime : 4
intervalTime : 4,
intervalAmount : 5
};

@@ -55,2 +38,4 @@ RED.nodes.createNode(this, config);

this.injType = tInj.timer;
} else if (config.injectTypeSelect === 'interval-amount') {
this.injType = tInj.intervalAmount;
} else {

@@ -60,6 +45,14 @@ this.injType = tInj.none;

this.intervalCount = config.intervalCount ? config.intervalCount : 0;
this.intervalCountType = (this.injType === tInj.interval || this.injType === tInj.intervalTime) ? (config.intervalCountType || 'num') : 'none';
if (this.injType === tInj.interval ||
this.injType === tInj.intervalTime ||
this.injType === tInj.intervalAmount) {
this.intervalCountType = (config.intervalCountType || 'num');
} else {
this.intervalCountType = 'none';
}
this.intervalCountMultiplier = config.intervalCountMultiplier ? config.intervalCountMultiplier : 60000;
if (this.injType === tInj.intervalTime ||
this.injType === tInj.intervalAmount ||
this.injType === tInj.timer) {

@@ -144,3 +137,3 @@ this.timeStartData = {

if (this.injType === tInj.intervalTime) {
if (this.injType === tInj.intervalTime ||this.injType === tInj.intervalAmount ) {
this.timeEndData = {

@@ -331,2 +324,20 @@ type: config.timeEndType,

/**
* get the schedule time
* @param {Date} time - time to schedule
* @param {number} [limit] - minimal time limit to schedule
* @returns {number} milliseconds until the defined Date
*/
node.tsGetNextScheduleTime = (time, limit) => {
const dNow = new Date();
let millisec = time.valueOf() - dNow.valueOf();
if (limit) {
while (millisec < limit) {
millisec += 86400000; // 24h
}
}
return millisec;
};
/**
* get the limitation for time

@@ -363,3 +374,3 @@ */

result.value = new Date(node.cacheYear + ((dNow >= node.cacheEnd) ? 1 : 0), node.cacheStart.getMonth(), node.cacheStart.getDate(), 0, 0, 1);
result.warnStatus = RED._('time-inject.errors.invalid-daterange') + ' [' + node.positionConfig.toDateString(result.value)+ ']';
result.errorStatus = RED._('time-inject.errors.invalid-daterange') + ' [' + node.positionConfig.toDateString(result.value)+ ']';
result.valid = false;

@@ -372,3 +383,3 @@ return result;

result.value = new Date(node.cacheYear, node.cacheStart.getMonth(), node.cacheStart.getDate(), 0, 0, 1);
result.warnStatus = RED._('time-inject.errors.invalid-daterange') + ' [' + node.positionConfig.toDateString(result.value)+ ']';
result.errorStatus = RED._('time-inject.errors.invalid-daterange') + ' [' + node.positionConfig.toDateString(result.value)+ ']';
result.valid = false;

@@ -382,10 +393,118 @@ return result;

node.initializeStartTimer = node => {
node.debug(`initializeStartTimer`);
if (!node.timeStartData) {
node.debug('initializeStartTimer - no start time data');
return false;
}
const dNow = new Date();
const nowTs = dNow.valueOf();
const startLimit = node.getTimeLimitation(dNow);
if (!startLimit.valid) {
node.debug('initializeStartTimer - start limited');
return false;
}
const initStartData = Object.assign({}, node.timeStartData);
initStartData.next = false;
const nextStartTimeData = node.positionConfig.getTimeProp(node, {}, initStartData);
if (nextStartTimeData.error || !hlp.isValidDate(nextStartTimeData.value)) {
node.debug(`initializeStartTimer - start time wrong ${ nextStartTimeData.error}`);
return false;
}
let millisec = nextStartTimeData.value.valueOf() - nowTs;
if (node.timeStartAltData) {
const initStartAltData = Object.assign({}, node.timeStartAltData);
initStartAltData.next = false;
const nextTimeAltData = node.positionConfig.getTimeProp(node, {}, initStartAltData);
if (!nextTimeAltData.error && hlp.isValidDate(nextTimeAltData.value)) {
const millisecAlt = nextTimeAltData.value.valueOf() - nowTs;
if (millisecAlt < millisec) {
millisec = millisecAlt;
}
}
}
if (millisec > 0 || !node.timeEndData) {
node.debug(`initializeStartTimer - start ${ millisec } in future or no end time`);
return false;
}
const initEndTime = Object.assign({},node.timeEndData);
initEndTime.next = false;
const nextEndTimeData = node.positionConfig.getTimeProp(node, {}, initEndTime);
if (nextEndTimeData.error || !hlp.isValidDate(nextEndTimeData.value)) {
node.debug(`initializeStartTimer - end time error ${ nextEndTimeData.error }`);
return false;
}
const millisecEnd = (nextEndTimeData.value.valueOf() - nowTs);
if (millisecEnd < 0) {
node.debug(`initializeStartTimer - end time ${ millisecEnd } in past`);
return false;
}
node.debug(`initializeStartTimer - starting interval!!`);
if (this.injType === tInj.intervalTime) {
node.getIntervalTime();
node.doStartInterval(); // starte Interval
} else if (node.injType === tInj.intervalAmount) {
node.IntervalCountMax = node.positionConfig.getFloatProp(node, null, node.intervalCountType, node.intervalCount, 0);
node.intervalTime = Math.floor((millisecEnd - millisec) / node.IntervalCountMax);
node.IntervalCountCurrent = 0;
node.doStartInterval(); // starte Interval
}
return true;
};
node.initialize = (node, doEmit) => {
node.debug(`initialize`);
switch (node.injType) {
case tInj.interval:
if (doEmit) {
node.emit('input', {
type: 'once/startup'
}); // will create timeout
}
node.debug('initialize - absolute Intervall');
node.getIntervalTime();
clearInterval(node.intervalObj);
node.status({
text: '↻' + Math.round(((node.intervalTime / 1000) + Number.EPSILON) * 10) / 10 + 's'
});
node.send(node.prepOutMsg({ type: 'interval-start' }));
node.intervalObj = setInterval(() => {
node.send(node.prepOutMsg({ type: 'interval' }));
}, node.intervalTime);
break;
case tInj.timer:
node.debug('initialize - timer');
if (doEmit) {
node.emit('input', {
type: 'once/startup'
}); // will create timeout
} else {
node.doCreateStartTimeout(node);
}
break;
case tInj.intervalTime:
case tInj.intervalAmount:
if (doEmit) {
node.emit('input', {
type: 'once/startup'
});
}
if (!node.initializeStartTimer(node)) {
node.doCreateStartTimeout(node);
}
break;
}
};
/**
* creates the end timeout
* @param {*} node - the node representation
* @returns {object} state or error
* get the end time in millisecond
* @param {*} node the node Data
*/
node.doCreateEndTimeout = node => {
node.getMillisecEnd = node => {
if (!node.timeEndData) {
return;
return null;
}

@@ -397,21 +516,31 @@ node.debug(`doCreateEndTimeout node.timeEndData=${util.inspect(node.timeEndData, { colors: true, compact: 10, breakLength: Infinity })}`);

}
node.nextEndTime = null;
let errorStatus = '';
const nextEndTimeData = node.positionConfig.getTimeProp(node, {}, node.timeEndData);
if (nextEndTimeData.error) {
errorStatus = 'could not evaluate end time';
node.nextEndTime = null;
node.debug('nextEndTimeData=' + util.inspect(nextEndTimeData, { colors: true, compact: 10, breakLength: Infinity }));
node.error(nextEndTimeData.error);
} else {
node.nextEndTime = nextEndTimeData.value;
return null;
}
node.nextEndTime = nextEndTimeData.value;
let millisecEnd = 1000 * 60 * 60 * 24; // 24h
if ((node.nextEndTime !== null) && (typeof node.nextEndTime !== 'undefined') && (errorStatus === '')) {
if ((node.nextEndTime !== null) && (typeof node.nextEndTime !== 'undefined')) {
// node.debug('timeout ' + node.nextEndTime + ' is in ' + millisec + 'ms');
millisecEnd = tsGetScheduleTime(node.nextEndTime, 10);
millisecEnd = node.tsGetNextScheduleTime(node.nextEndTime, 10);
}
return millisecEnd;
};
if (millisecEnd> 345600000) {
/**
* creates the end timeout
* @param {*} node - the node representation
* @returns {object} state or error
*/
node.doCreateEndTimeout = (node, millisecEnd) => {
millisecEnd = millisecEnd || node.getMillisecEnd(node);
if (millisecEnd === null) {
return;
}
if (millisecEnd > 345600000) {
millisecEnd = Math.min((millisecEnd - 129600000), 2147483646);

@@ -427,3 +556,3 @@ node.timeOutEndObj = setTimeout(() => {

node.intervalObj = null;
node.doCreateStartTimeout(node, false);
node.doCreateStartTimeout(node);
}, millisecEnd);

@@ -457,3 +586,3 @@ }; // doCreateEndTimeout

for (let i = 0; i < node.props.length; i++) {
node.debug(`prepOutMsg-${i} node.props[${i}]=${util.inspect(node.props[i], { colors: true, compact: 10, breakLength: Infinity })}`);
// node.debug(`prepOutMsg-${i} node.props[${i}]=${util.inspect(node.props[i], { colors: true, compact: 10, breakLength: Infinity })}`);
const res = node.positionConfig.getOutDataProp(this, msg, node.props[i], dNow);

@@ -475,2 +604,35 @@ if (res === null || (typeof res === 'undefined')) {

/**
* get and validate a given interval
*/
node.getIntervalTime = () => {
node.intervalTime = node.positionConfig.getFloatProp(node, null, node.intervalCountType, node.intervalCount, 0);
if (node.intervalTime <= 0) {
throw new Error('Interval wrong!');
} else {
if (node.intervalCountMultiplier > 0) {
node.intervalTime = Math.floor(node.intervalTime * node.intervalCountMultiplier);
}
}
};
/**
* start an Intervall
*/
node.doStartInterval = () => {
node.timeOutStartObj = null;
node.doCreateEndTimeout(node);
clearInterval(node.intervalObj);
node.doSetStatus(node, 'green');
node.send(node.prepOutMsg({ type: 'interval-time-start' }));
node.intervalObj = setInterval(() => {
node.IntervalCountCurrent++;
if (node.IntervalCountMax < 1) {
node.send(node.prepOutMsg({ type: 'interval-time' }));
} else if (node.IntervalCountCurrent < node.IntervalCountMax) {
node.send(node.prepOutMsg({ type: 'interval-amount' }));
}
}, node.intervalTime);
};
/**
* creates the timeout

@@ -481,4 +643,9 @@ * @param {*} node - the node representation

*/
node.doCreateStartTimeout = (node, _onInit) => {
// node.debug(`doCreateStartTimeout _onInit=${_onInit} node.timeStartData=${util.inspect(node.timeStartData, { colors: true, compact: 10, breakLength: Infinity })}`);
node.doCreateStartTimeout = node => {
node.debug(`doCreateStartTimeout node.timeStartData=${util.inspect(node.timeStartData, { colors: true, compact: 10, breakLength: Infinity })}`);
if (node.injType === tInj.none ||
node.injType === tInj.interval) {
return;
}
node.nextStartTime = null;

@@ -491,35 +658,17 @@ node.nextStartTimeAlt = null;

}
delete node.intervalTime;
if (node.injType === tInj.none) {
return { state:'ok', done: true };
if (node.injType === tInj.intervalTime) {
node.IntervalCountMax = 0;
node.getIntervalTime();
}
if (node.injType === tInj.interval ||
node.injType === tInj.intervalTime) {
node.intervalTime = node.positionConfig.getFloatProp(node, null, node.intervalCountType, node.intervalCount, 0);
if (node.intervalTime <= 0) {
throw new Error('Interval wrong!');
} else {
if (node.intervalCountMultiplier > 0) {
node.intervalTime = Math.floor(node.intervalTime * node.intervalCountMultiplier);
}
}
if (node.injType === tInj.intervalAmount) {
node.IntervalCountMax = node.positionConfig.getFloatProp(node, null, node.intervalCountType, node.intervalCount, 0);
delete node.intervalTime;
}
// node.debug(`doCreateStartTimeout2 node.intervalTime=${util.inspect(node.intervalTime, { colors: true, compact: 10, breakLength: Infinity })}`);
if (!node.timeStartData) {
node.debug('doCreateStartTimeout - absolute Intervall');
clearInterval(node.intervalObj);
node.send(node.prepOutMsg({ type: 'interval-start' }));
node.intervalObj = setInterval(() => {
node.send(node.prepOutMsg({ type: 'interval' }));
}, node.intervalTime);
return { state:'ok', done: true };
}
let fill = 'yellow';
let shape = 'dot';
let errorStatus = '';
let warnStatus = '';
node.timeStartData.isAltAvailable = false;

@@ -535,52 +684,44 @@ node.timeStartData.isAltFirst = false;

if (nextStartTimeData.error) {
errorStatus = 'could not evaluate time';
if (_onInit === true) {
return { state: 'error', done: false, statusMsg: errorStatus, errorMsg: nextStartTimeData.error };
}
node.debug('node.nextStartTimeData=' + util.inspect(nextStartTimeData, { colors: true, compact: 10, breakLength: Infinity }));
node.error(nextStartTimeData.error);
} else {
node.nextStartTime = nextStartTimeData.value;
isFixedTime = isFixedTime && nextStartTimeData.fix;
hlp.handleError(node, nextStartTimeData.error, null, 'could not evaluate time');
return;
}
node.nextStartTime = nextStartTimeData.value;
isFixedTime = isFixedTime && nextStartTimeData.fix;
if (node.timeStartAltData) {
node.timeStartAltData.now = node.timeStartData.now;
node.debug(`node.timeStartAltData=${util.inspect(node.timeStartAltData, { colors: true, compact: 10, breakLength: Infinity })}`);
const nextTimeAltData = node.positionConfig.getTimeProp(node, {}, node.timeStartAltData);
if (node.timeStartAltData) {
node.timeStartAltData.now = node.timeStartData.now;
node.debug(`node.timeStartAltData=${util.inspect(node.timeStartAltData, { colors: true, compact: 10, breakLength: Infinity })}`);
const nextTimeAltData = node.positionConfig.getTimeProp(node, {}, node.timeStartAltData);
if (nextTimeAltData.error) {
errorStatus = 'could not evaluate alternate time';
isFixedTime = false;
if (_onInit === true) {
return { state:'error', done: false, statusMsg: errorStatus, errorMsg: nextTimeAltData.error};
}
node.debug('nextTimeAltData=' + util.inspect(nextTimeAltData, { colors: true, compact: 10, breakLength: Infinity }));
node.error(nextTimeAltData.error);
} else {
node.nextStartTimeAlt = nextTimeAltData.value;
isFixedTime = isFixedTime && nextTimeAltData.fix;
if (!hlp.isValidDate(node.nextStartTimeAlt)) {
hlp.handleError(this, 'Invalid time format of alternate time "' + node.nextStartTimeAlt + '"', undefined, 'internal error!');
} else {
node.timeStartData.isAltAvailable = true;
}
}
if (nextTimeAltData.error) {
isFixedTime = false;
node.debug('nextTimeAltData=' + util.inspect(nextTimeAltData, { colors: true, compact: 10, breakLength: Infinity }));
hlp.handleError(node, nextTimeAltData.error, null, 'could not evaluate alternate time');
return;
}
node.nextStartTimeAlt = nextTimeAltData.value;
isFixedTime = isFixedTime && nextTimeAltData.fix;
if (!hlp.isValidDate(node.nextStartTimeAlt)) {
hlp.handleError(this, 'Invalid time format of alternate time "' + node.nextStartTimeAlt + '"', undefined, 'internal error!');
} else {
node.timeStartData.isAltAvailable = true;
}
}
} else {
warnStatus = startLimit.warnStatus;
node.nextStartTime = startLimit.value;
hlp.handleError(this, startLimit.errorStatus);
return;
}
if ((node.nextStartTime) && (errorStatus === '')) {
if (node.nextStartTime) {
if (!hlp.isValidDate(node.nextStartTime)) {
hlp.handleError(this, 'Invalid time format "' + node.nextStartTime + '"', undefined, 'internal error!');
return { state:'error', done: false, statusMsg: 'Invalid time format!', errorMsg: 'Invalid time format'};
return;
}
let millisec = tsGetScheduleTime(node.nextStartTime, 10);
let millisec = node.tsGetNextScheduleTime(node.nextStartTime, 10);
if (node.timeStartData.isAltAvailable) {
shape = 'ring';
const millisecAlt = tsGetScheduleTime(node.nextStartTimeAlt, 10);
const millisecAlt = node.tsGetNextScheduleTime(node.nextStartTimeAlt, 10);
if (millisecAlt < millisec) {

@@ -607,14 +748,14 @@ millisec = millisecAlt;

} else if (this.injType === tInj.intervalTime) {
node.timeEndData.now = node.nextStartTime;
node.timeOutStartObj = setTimeout(() => {
node.timeOutStartObj = null;
node.doCreateEndTimeout(node);
clearInterval(node.intervalObj);
node.send(node.prepOutMsg({ type: 'interval-time-start' }));
node.intervalObj = setInterval(() => {
node.send(node.prepOutMsg({ type: 'interval-time' }));
}, node.intervalTime);
}, millisec);
node.debug('intervalTime - timeout ' + node.nextStartTime + ' is in ' + millisec + 'ms (isAlt=' + node.timeStartData.isAltAvailable + ' isAltFirst=' + node.timeStartData.isAltFirst + ')');
node.timeOutStartObj = setTimeout(node.doStartInterval, millisec);
fill = 'grey';
} else if (this.injType === tInj.intervalAmount) {
node.debug('intervalAmount - timeout ' + node.nextStartTime + ' is in ' + millisec + 'ms (isAlt=' + node.timeStartData.isAltAvailable + ' isAltFirst=' + node.timeStartData.isAltFirst + ')');
const millisecEnd = node.getMillisecEnd(node);
node.intervalTime = Math.floor((millisecEnd - millisec) / node.IntervalCountMax);
node.IntervalCountCurrent = 0;
node.timeOutStartObj = setTimeout(node.doStartInterval, millisec);
fill = 'grey';
} else { // this.injType === tInj.timer
// node.debug('timeout ' + node.nextStartTime + ' is in ' + millisec + 'ms (isAlt=' + node.timeStartData.isAltAvailable + ' isAltFirst=' + node.timeStartData.isAltFirst + ')');
node.debug('timeout ' + node.nextStartTime + ' is in ' + millisec + 'ms (isAlt=' + node.timeStartData.isAltAvailable + ' isAltFirst=' + node.timeStartData.isAltFirst + ')');
node.timeOutStartObj = setTimeout(() => {

@@ -646,3 +787,3 @@ // node.debug(`timeOutStartObj isAlt=${isAlt} isAltFirst=${node.timeStartData.isAltFirst}`);

node.doRecalcStartTimeOut();
return { state:'recalc', done: true };
return;
}

@@ -653,7 +794,6 @@ }

node.emit('input', msg);
return { state: 'emit', done: true };
}, millisec);
fill = 'green';
if (!isFixedTime && !node.intervalObj && (_onInit !== true)) {
if (!isFixedTime && !node.intervalObj) {
node.intervalObj = setInterval(() => {

@@ -669,18 +809,7 @@ node.debug('retriggered');

}
node.doSetStatus(node, fill, shape);
};
if ((errorStatus !== '')) {
node.status({
fill: 'red',
shape,
text: errorStatus + ((node.intervalObj) ? ' ↺🖩' : '')
});
return { state:'error', done: false, statusMsg: errorStatus, errorMsg: errorStatus };
// if an error occurred, will retry in 10 minutes. This will prevent errors on initialization.
} else if ((warnStatus !== '')) {
node.status({
fill: 'red',
shape,
text: warnStatus + ((node.intervalObj) ? ' ↺🖩' : '')
});
} else if (node.nextStartTimeAlt && node.timeOutStartObj) {
node.doSetStatus = (node, fill, shape) => {
if (node.nextStartTimeAlt && node.timeOutStartObj) {
if (node.timeStartData.isAltFirst) {

@@ -700,11 +829,31 @@ node.status({

} else if (node.nextStartTime && node.timeOutStartObj) {
let txt = node.positionConfig.toDateTimeString(node.nextStartTime);
if (node.nextEndTime) {
txt += ' - ';
txt += node.positionConfig.toDateTimeString(node.nextEndTime);
}
if (node.intervalTime) {
txt += ' ↻';
txt += Math.round(((node.intervalTime / 1000) + Number.EPSILON) * 10) / 10;
txt += 's';
}
node.status({
fill,
shape,
text: node.positionConfig.toDateTimeString(node.nextStartTime)
text: txt
});
} else if (node.intervalTime) {
let txt = '↻' + Math.round(((node.intervalTime / 1000) + Number.EPSILON) * 10) / 10 + 's';
if (node.nextEndTime) {
txt += ' -> ';
txt += node.positionConfig.toDateTimeString(node.nextEndTime);
}
node.status({
fill,
shape,
text: txt
});
} else {
node.status({});
}
return { state:'ok', done: true };
};

@@ -722,3 +871,5 @@

}
node.doCreateStartTimeout(node, false);
if (node.injType === tInj.timer) {
node.doCreateStartTimeout(node);
}
send(node.prepOutMsg(msg));

@@ -766,7 +917,14 @@ if (msg.payload === null || (typeof msg.payload === 'undefined')) {

});
node.onceTimeout = setTimeout(() => {
node.emit('input', {
type: 'once/startup'
}); // will create timeout
try {
node.initialize(node, true);
} catch (err) {
node.error(err.message);
node.log(util.inspect(err, Object.getOwnPropertyNames(err)));
node.status({
fill: 'red',
shape: 'ring',
text: RED._('node-red-contrib-sun-position/position-config:errors.error-title')
});
}
}, (config.onceDelay || 0.1) * 1000);

@@ -778,26 +936,3 @@ return;

try {
const createTO = node.doCreateStartTimeout(node, true);
if (createTO.done !== true) {
if (createTO.errorMsg) {
node.warn(RED._('node-red-contrib-sun-position/position-config:errors.warn-init', { message: createTO.errorMsg, time: 6}));
}
node.onceTimeout2 = setTimeout(() => {
try {
node.doCreateStartTimeout(node);
} catch (err) {
node.error(err.message);
node.log(util.inspect(err, Object.getOwnPropertyNames(err)));
node.status({
fill: 'red',
shape: 'ring',
text: RED._('node-red-contrib-sun-position/position-config:errors.error-title')
});
}
}, 360000); // 6 Minuten
node.status({
fill: 'red',
shape: 'ring',
text: RED._('node-red-contrib-sun-position/position-config:errors.error-init', { message: createTO.statusMsg, time: '6min'})
});
}
node.initialize(node, false);
} catch (err) {

@@ -812,3 +947,3 @@ node.error(err.message);

}
}, 200 + Math.floor(Math.random() * 600));
}, 400 + Math.floor(Math.random() * 600));
} catch (err) {

@@ -815,0 +950,0 @@ node.error(err.message);

@@ -93,6 +93,6 @@ /********************************************

* @param {*} msg - the messege object
* @param {*} config - the configuration
* @param {Date|null} dNow - the current time
* @returns {object} containing start and end Dates
*/
function calcWithinTimes(node, msg, config, dNow) {
function calcWithinTimes(node, msg, dNow) {
// node.debug('calcWithinTimes');

@@ -110,16 +110,65 @@ const result = {

if (config.timeDays && !config.timeDays.includes(dNow.getDay())) {
node.debug('invalid Day config. today=' + dNow.getDay() + ' timeDays=' + util.inspect(config.timeDays, Object.getOwnPropertyNames(config.timeDays)));
if (node.timeRestrictions.type !== 'none') {
try {
if (node.timeRestrictions.type === 'jsonata') {
if (!node.timeRestrictions.expr) {
node.timeRestrictions.expr = this.getJSONataExpression(node, node.timeRestrictions.value);
}
node.timeRestrictions.data = RED.util.evaluateJSONataExpression(node.timeRestrictions.expr, msg);
} else {
node.timeRestrictions.data = RED.util.evaluateNodeProperty(node.timeRestrictions.value, node.timeRestrictions.type, node, msg);
}
if (typeof node.timeRestrictions.data === 'object') {
// node.debug(util.inspect(node.timeRestrictions, Object.getOwnPropertyNames(node.timeRestrictions)));
if (Object.prototype.hasOwnProperty.call(node.timeRestrictions.data,'days')) {
node.timeDays = node.timeRestrictions.data.days;
} else {
delete node.timeDays;
}
if (Object.prototype.hasOwnProperty.call(node.timeRestrictions.data,'months')) {
node.timeMonths = node.timeRestrictions.data.months;
} else {
delete node.timeMonths;
}
if (Object.prototype.hasOwnProperty.call(node.timeRestrictions.data,'startDate') && node.timeRestrictions.data.startDate !== '') {
node.timeStartDate = node.timeRestrictions.data.startDate;
} else {
delete node.timeStartDate;
}
if (Object.prototype.hasOwnProperty.call(node.timeRestrictions.data,'endDate') && node.timeRestrictions.data.endDate !== '') {
node.timeEndDate = node.timeRestrictions.data.endDate;
} else {
delete node.timeEndDate;
}
if (Object.prototype.hasOwnProperty.call(node.timeRestrictions.data,'onlyOddDays')) {
node.timeOnlyOddDays = node.timeRestrictions.data.onlyOddDays;
} else {
delete node.timeOnlyOddDays;
}
if (Object.prototype.hasOwnProperty.call(node.timeRestrictions.data,'onlyEvenDays')) {
node.timeOnlyEvenDays = node.timeRestrictions.data.onlyEvenDays;
} else {
delete node.timeOnlyEvenDays;
}
}
} catch (err) {
node.debug(util.inspect(err, Object.getOwnPropertyNames(err)));
node.error(err);
}
}
if ((typeof node.timeDays !== 'undefined') && !node.timeDays.includes(dNow.getDay())) {
node.debug('invalid Day config. today=' + dNow.getDay() + ' timeDays=' + util.inspect(node.timeDays, Object.getOwnPropertyNames(node.timeDays)));
result.warn = RED._('within-time-switch.errors.invalid-day');
return result;
}
if (config.timeMonths && !config.timeMonths.includes(dNow.getMonth())) {
node.debug('invalid Month config. today=' + dNow.getMonth() + ' timeMonths=' + util.inspect(config.timeMonths, Object.getOwnPropertyNames(config.timeMonths)));
if ((typeof node.timeMonths !== 'undefined') && !node.timeMonths.includes(dNow.getMonth())) {
node.debug('invalid Month config. today=' + dNow.getMonth() + ' timeMonths=' + util.inspect(node.timeMonths, Object.getOwnPropertyNames(node.timeMonths)));
result.warn = RED._('within-time-switch.errors.invalid-month');
return result;
}
if (config.timedatestart || config.timedateend) {
if ((typeof node.timeStartDate !== 'undefined') || (typeof node.timeEndDate !== 'undefined')) {
let dStart,dEnd;
if (config.timedatestart) {
dStart = new Date(config.timedatestart);
if (typeof node.timeStartDate !== 'undefined') {
dStart = new Date(node.timeStartDate);
dStart.setFullYear(dNow.getFullYear());

@@ -130,4 +179,4 @@ dStart.setHours(0, 0, 0, 1);

}
if (config.timedateend) {
dEnd = new Date(config.timedateend);
if (typeof node.timeEndDate !== 'undefined') {
dEnd = new Date(node.timeEndDate);
dEnd.setFullYear(dNow.getFullYear());

@@ -175,2 +224,12 @@ dEnd.setHours(23, 59, 59, 999);

if (result.altStartTime) {
// node.debug(`using alternate start time node.timeStart=${ util.inspect(node.timeStart, Object.getOwnPropertyNames(node.timeStart))}, config.timeStartAlt=${ util.inspect(node.timeStartAlt, Object.getOwnPropertyNames(node.timeStartAlt))}`);
result.start = node.positionConfig.getTimeProp(node, msg, node.timeStartAlt);
result.startSuffix = '⎇ ';
} else if (msg || (node.timeStart.type !== 'msg')) {
// node.debug(`using alternate start time node.timeStart=${ util.inspect(node.timeStart, Object.getOwnPropertyNames(node.timeStart))}`);
result.start = node.positionConfig.getTimeProp(node, msg, node.timeStart);
}
if (result.altEndTime) {

@@ -187,43 +246,9 @@ // node.debug('alternate end times enabled ' + node.propertyEnd.type + '.' + node.propertyEnd.value);

if (result.altStartTime && config.startTimeAltType !== 'none') {
// node.debug(`using alternate start time config.startTimeType=${ config.startTimeType},config.startTime=${ config.startTime}, result.altStartTime=${ result.altStartTime}, config.startTimeAltType=${ config.startTimeAltType}`);
result.start = node.positionConfig.getTimeProp(node, msg, {
type: config.startTimeAltType,
value : config.startTimeAlt,
offsetType : config.startOffsetAltType,
offset : config.startOffsetAlt,
multiplier : config.startOffsetAltMultiplier
});
result.startSuffix = '⎇ ';
} else if (msg || (node.startTimeType !== 'msg')) {
// node.debug(`using standard start time config.startTimeType=${ config.startTimeType},config.startTime=${ config.startTime}, result.altStartTime=${ result.altStartTime}, config.startTimeAltType=${ config.startTimeAltType}`);
result.start = node.positionConfig.getTimeProp(node, msg, {
type: config.startTimeType,
value : config.startTime,
offsetType : config.startOffsetType,
offset : config.startOffset,
multiplier : config.startOffsetMultiplier
});
}
if (result.altEndTime && config.endTimeAltType !== 'none') {
// node.debug(`using alternate end time config.endTimeType=${ config.endTimeType},config.endTime=${ config.endTime}, result.altEndTime=${ result.altEndTime}, config.endTimeAltType=${ config.endTimeAltType}`);
result.end = node.positionConfig.getTimeProp(node, msg, {
type: config.endTimeAltType,
value : config.endTimeAlt,
offsetType : config.endOffsetAltType,
offset : config.endOffsetAlt,
multiplier : config.endOffsetAltMultiplier
});
if (result.altEndTime) {
// node.debug(`using alternate start time node.timeEnd=${ util.inspect(node.timeEnd, Object.getOwnPropertyNames(node.timeEnd))}, config.timeEndAlt=${ util.inspect(node.timeEndAlt, Object.getOwnPropertyNames(node.timeEndAlt))}`);
result.end = node.positionConfig.getTimeProp(node, msg, node.timeEndAlt);
result.endSuffix = ' ⎇';
} else if (msg || (node.endTimeType !== 'msg')) {
// node.debug(`using standard end time config.endTimeType=${ config.endTimeType},config.endTime=${ config.endTime}, result.altEndTime=${ result.altEndTime}, config.endTimeAltType=${ config.endTimeAltType}`);
result.end = node.positionConfig.getTimeProp(node, msg, {
type: config.endTimeType,
value : config.endTime,
offsetType : config.endOffsetType,
offset : config.endOffset,
multiplier : config.endOffsetMultiplier
});
} else if (msg || (node.timeEnd.type !== 'msg')) {
// node.debug(`using alternate start time node.timeEnd=${ util.inspect(node.timeEnd, Object.getOwnPropertyNames(node.timeEnd))}`);
result.end = node.positionConfig.getTimeProp(node, msg, node.timeEnd);
}

@@ -245,2 +270,26 @@

this.timeStart = {
type: config.startTimeType,
value : config.startTime,
offsetType : config.startOffsetType,
offset : config.startOffset,
multiplier : config.startOffsetMultiplier
};
this.timeEnd = {
type: config.endTimeType,
value : config.endTime,
offsetType : config.endOffsetType,
offset : config.endOffset,
multiplier : config.endOffsetMultiplier
};
this.timeStartAlt = {
type: config.startTimeAltType || 'none',
value : config.startTimeAlt,
offsetType : config.startOffsetAltType,
offset : config.startOffsetAlt,
multiplier : config.startOffsetAltMultiplier
};
this.propertyStartOperator = config.propertyStartCompare || 'true';

@@ -263,3 +312,15 @@ this.propertyStart = {

}
if (this.propertyStart.type === 'none' || this.timeStartAlt.type === 'none') {
this.propertyStart.type = 'none';
delete this.timeStartAlt;
}
this.timeEndAlt = {
type: config.endTimeAltType || 'none',
value : config.endTimeAlt,
offsetType : config.endOffsetAltType,
offset : config.endOffsetAlt,
multiplier : config.endOffsetAltMultiplier
};
this.propertyEndOperator = config.propertyEndCompare || 'true';

@@ -282,27 +343,54 @@ this.propertyEnd = {

}
this.timeOnlyEvenDays = config.timeOnlyEvenDays;
this.timeOnlyOddDays = config.timeOnlyOddDays;
if (config.timeDays === '') {
throw new Error('No valid days given! Please check settings!');
} else if (!config.timeDays || config.timeDays === '*') {
config.timeDays = null;
} else {
config.timeDays = config.timeDays.split(',');
config.timeDays = config.timeDays.map( e => parseInt(e) );
if (this.propertyEnd.type === 'none' || this.timeEndAlt.type === 'none') {
this.propertyEnd.type = 'none';
delete this.timeEndAlt;
}
if (config.timeMonths === '') {
throw new Error('No valid month given! Please check settings!');
} else if (!config.timeMonths || config.timeMonths === '*') {
config.timeMonths = null;
} else {
config.timeMonths = config.timeMonths.split(',');
config.timeMonths = config.timeMonths.map( e => parseInt(e) );
this.timeRestrictions = {
type: config.timeRestrictionsType || 'none',
value : config.timeRestrictions
};
if (this.timeRestrictions.type === 'jsonata') {
try {
this.timeRestrictions.expr = this.positionConfig.getJSONataExpression(this, this.timeRestrictions.value);
} catch (err) {
this.error(RED._('node-red-contrib-sun-position/position-config:errors.invalid-expr', { error:err.message }));
this.timeRestrictions.expr = null;
}
}
if (this.timeOnlyEvenDays && this.timeOnlyOddDays) {
this.timeOnlyEvenDays = false;
this.timeOnlyOddDays = false;
if (this.timeRestrictions.type !== 'none') {
this.timeOnlyEvenDays = config.timeOnlyEvenDays;
this.timeOnlyOddDays = config.timeOnlyOddDays;
if (typeof config.timedatestart !== undefined && config.timedatestart !== '') {
this.timeStartDate = new Date(config.timedatestart);
}
if (typeof config.timedateend !== undefined && config.timedateend !== '') {
this.timeEndDate = new Date(config.timedateend);
}
if (config.timeDays === '') {
throw new Error('No valid days given! Please check settings!');
} else if (!config.timeDays || config.timeDays === '*') {
// config.timeDays = null;
delete this.timeDays;
} else {
this.timeDays = config.timeDays.split(',');
this.timeDays = config.timeDays.map( e => parseInt(e) );
}
if (config.timeMonths === '') {
throw new Error('No valid month given! Please check settings!');
} else if (!config.timeMonths || config.timeMonths === '*') {
// config.timeMonths = null;
delete this.timeMonths;
} else {
this.timeMonths = config.timeMonths.split(',');
this.timeMonths = config.timeMonths.map( e => parseInt(e) );
}
if (this.timeOnlyEvenDays && this.timeOnlyOddDays) {
this.timeOnlyEvenDays = false;
this.timeOnlyOddDays = false;
}
}

@@ -314,3 +402,2 @@

this.on('input', function (msg, send, done) {

@@ -330,5 +417,4 @@ // If this is pre-1.0, 'done' will be undefined

// this.debug('self ' + util.inspect(this, { colors: true, compact: 10, breakLength: Infinity }));
// this.debug('config ' + util.inspect(config, { colors: true, compact: 10, breakLength: Infinity }));
const now = getIntDate(config.tsCompare, msg, node);
const result = calcWithinTimes(this, msg, config, now);
const result = calcWithinTimes(this, msg, now);

@@ -341,3 +427,3 @@ if (result.valid && result.start.value && result.end.value) {

const cmpNow = hlp.getTimeNumberUTC(now);
setstate(this, result);
setstate(node, result);
if (msg.withinTimeStart.id < msg.withinTimeEnd.id) {

@@ -344,0 +430,0 @@ if (cmpNow >= msg.withinTimeStart.id && cmpNow < msg.withinTimeEnd.id) {

{
"name": "node-red-contrib-sun-position",
"version": "1.1.7",
"version": "1.2.0",
"description": "NodeRED nodes to get sun and moon position",

@@ -94,5 +94,5 @@ "keywords": [

"devDependencies": {
"eslint": "^7.9.0",
"eslint-plugin-html": "^6.1.0",
"eslint-plugin-jsdoc": "^30.4.2",
"eslint": "^7.13.0",
"eslint-plugin-html": "^6.1.1",
"eslint-plugin-jsdoc": "^30.7.8",
"eslint-plugin-json": "^2.1.2",

@@ -99,0 +99,0 @@ "eslint-plugin-node": "^11.1.0"

@@ -15,3 +15,3 @@ # node-red-contrib-sun-position for NodeRED

Additional you can get sun and moon position or to control a flow by sun or moon position. It is ideal for usage of control smart home, but also for all other time based flow control.
There is also a roller [blind control](blind_control.md) node and a [clock timer](clock_timer.md) node. The [blind control](blind_control.md) node can determine the position of the roller shutter by time or position of the sun. The [timer](clock_timer.md) can send different payloads depending on the time.
There is also a roller [blind control](https://github.com/rdmtc/node-red-contrib-sun-position/wiki/blind-control) node and a [clock timer](https://github.com/rdmtc/node-red-contrib-sun-position/wiki/clock-timer) node. The [blind control](https://github.com/rdmtc/node-red-contrib-sun-position/wiki/blind-control) node can determine the position of the roller shutter by time or position of the sun. The [timer](https://github.com/rdmtc/node-red-contrib-sun-position/wiki/clock-timer) can send different payloads depending on the time.

@@ -67,3 +67,3 @@ ![nodes](https://user-images.githubusercontent.com/12692680/70033601-19d46a00-15b0-11ea-9e36-a7843e20ff85.png)

- [Implemented Nodes](https://github.com/rdmtc/node-red-contrib-sun-position/wiki)
- [sun-position](https://github.com/rdmtc/node-red-contrib-sun-position/wiki/moon-position)
- [sun-position](https://github.com/rdmtc/node-red-contrib-sun-position/wiki/sun-position)
- [moon-position](https://github.com/rdmtc/node-red-contrib-sun-position/wiki/moon-position)

@@ -70,0 +70,0 @@ - [time-inject](https://github.com/rdmtc/node-red-contrib-sun-position/wiki/time-inject)

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc