Skip to content

Commit

Permalink
Merge pull request #8 from Sineos/retrigger
Browse files Browse the repository at this point in the history
  • Loading branch information
Sineos committed Mar 18, 2024
2 parents bc786a7 + 75e1f34 commit 1abb2d1
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 95 deletions.
8 changes: 4 additions & 4 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
module.exports = {
"env": {
"node": false,
"node": true,
"browser": true,
"commonjs": true,
"jquery": true
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 7,
"ecmaVersion": 'latest',
"sourceType": "script"
},
"rules": {
Expand Down Expand Up @@ -60,7 +60,7 @@ module.exports = {
"id-match": "error",
"indent": [
"error",
2, { "VariableDeclarator": 2 }
2, { "VariableDeclarator": 1, "SwitchCase": 1 }
],
"init-declarations": "off",
"jsx-quotes": "error",
Expand Down Expand Up @@ -174,7 +174,7 @@ module.exports = {
],
"object-curly-spacing": [
"error",
"never"
"always"
],
"object-property-newline": [
"error",
Expand Down
11 changes: 7 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
{
"name": "node-red-contrib-timeouttrigger",
"version": "0.0.4",
"description": "This node will pipe any received `msg` to its output. At the same time a time-out interval is set. Upon time-out a defined payload is send. Each new message will reset the time-out.",
"version": "0.0.6",
"description": "This node will pipe any received `msg` to its output. At the same time a time-out interval is set. Upon time-out a defined payload is sent. Each new message will reset the time-out.",
"main": "timeouttrigger.js",
"scripts": {},
"repository": {
"type": "git",
"url": "git+https://github.com/Sineos/node-red-contrib-timeouttrigger.git"
Expand All @@ -24,5 +23,9 @@
"bugs": {
"url": "https://github.com/Sineos/node-red-contrib-timeouttrigger/issues"
},
"homepage": "https://github.com/Sineos/node-red-contrib-timeouttrigger#readme"
"homepage": "https://github.com/Sineos/node-red-contrib-timeouttrigger#readme",
"engines": {
"node": ">=6",
"node-red": ">=1.3.3"
}
}
137 changes: 100 additions & 37 deletions timeouttrigger.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,68 +15,131 @@
-->

<script type="text/javascript">
RED.nodes.registerType('timeouttrigger',{
RED.nodes.registerType('timeouttrigger', {
category: 'function',
color:"#E6E0F8",
color: "#E6E0F8",
defaults: {
ontimeouttype: {value:'str'},
//ontimeoutval: {value:"0", validate:RED.validators.typedInput("ontimeouttype")},
ontimeoutval: {value:"",required:true},
duration: {value:"",required:true,validate:RED.validators.number()},
units: {value:"s"},
passthrough: {value: true},
name: {value:""}
OnTimeoutType: { value: 'str' },
OnTimeoutVal: { value: "", required: true },
duration: { value: "", required: true, validate: RED.validators.number() },
units: { value: "s" },
passthrough: { value: true },
sendTimeoutValue: { value: "once" },
interval: { value: "", validate: function(value) {
return ($("#node-input-interval-row").is(":visible") && value === "") ? false : true; }},
IntervalUnits: { value: "s" },
name: { value: "" }
},
inputs:1,
outputs:1,
inputs: 1,
outputs: 1,
icon: "timeouttrigger.png",
label: function() {
return this.name + " " + this.duration + this.units || "timeouttrigger" + " " + this.duration + this.units;
label: function () {
var label = this.name + " " + this.duration + this.units || "timeouttrigger" + " " + this.duration + this.units;
if (this.sendTimeoutValue === "continuously" && this.interval) {
label += " / " + this.interval + this.IntervalUnits;
}
return label;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
labelStyle: function () {
return this.name ? "node_label_italic" : "";
},
oneditprepare: function() {
$("#node-input-ontimeoutval").typedInput({
default: 'str',
typeField: $("#node-input-ontimeouttype"),
types:['str','num','bool']
oneditprepare: function () {
const intervalRow = $("#node-input-interval-row");
const inputSendTimeoutValue = $("#node-input-sendTimeoutValue");
const inputOnTimeoutVal = $("#node-input-OnTimeoutVal");
const inputUnits = $("#node-input-units");
const inputIntervalUnits = $("#node-input-IntervalUnits");

inputUnits.typedInput({type:"units", types:[{
value: "units",
options: [
{ value: "ms", label: "Miliseconds"},
{ value: "s", label: "Seconds"},
{ value: "min", label: "Minutes"},
{ value: "hr", label: "Hours"},
]
}]})

inputIntervalUnits.typedInput({type:"IntervalUnits", types:[{
value: "IntervalUnits",
options: [
{ value: "ms", label: "Miliseconds"},
{ value: "s", label: "Seconds"},
{ value: "min", label: "Minutes"},
{ value: "hr", label: "Hours"},
]
}]})

inputSendTimeoutValue.typedInput({type:"sendTimeoutValue", types:[{
value: "sendTimeoutValue",
options: [
{ value: "once", label: "Once"},
{ value: "continuously", label: "Continuously"},
]
}]})

inputOnTimeoutVal.typedInput({
default: "str",
typeField: $("#node-input-OnTimeoutType"),
types: ["str", "num", "bool"]
});
if (typeof this.passthrough === 'undefined'){

if (typeof this.passthrough === 'undefined') {
this.passthrough = true;
$("#node-input-passthrough").prop('checked', true);
$("#node-input-passthrough").prop("checked", true);
}

if (typeof this.sendTimeoutValue === 'undefined') {
this.sendTimeoutValue = "once";
inputSendTimeoutValue.val("once");
}

// show/hide the interval field based on the selection
function toggleIntervalField() {
intervalRow.toggle(inputSendTimeoutValue.val() === "continuously");
}

// Call the toggleIntervalField initially
toggleIntervalField();

// Event listener for the dropdown change event
inputSendTimeoutValue.on("change", toggleIntervalField);
}
});
</script>

<script type="text/x-red" data-template-name="timeouttrigger">
<div class="form-row">
<label style="width: 180px !important" for="node-input-duration"><i class="fa fa-clock-o"></i> Time-Out</label>
<input type="text" id="node-input-duration" style="text-align:end; width:100px !important">
<select id="node-input-units" style="width:140px !important">
<option value="ms">Miliseconds</option>
<option value="s">Seconds</option>
<option value="min">Minutes</option>
<option value="hr">Hours</option>
</select>
<label style="width: 180px !important" for="node-input-duration"><i class="fa fa-clock-o"></i> Time-Out</label>
<input type="text" id="node-input-duration" placeholder="Time-Out" style="text-align:end; width:100px !important">
<input type="text" id="node-input-units" style="width:140px !important">
</div>
<div class="form-row">
<label style="width: 180px !important" for="node-input-ontimeouttype"><i class="fa fa-feed"></i> Send on Time-Out</label>
<input type="hidden" id="node-input-ontimeouttype">
<input type="text" id="node-input-ontimeoutval" placeholder="Time-Out Value" style="text-align:end; width:245px !important">
<label style="width: 180px !important" for="node-input-OnTimeoutType"><i class="fa fa-feed"></i> Send on Time-Out</label>
<input type="hidden" id="node-input-OnTimeoutType">
<input type="text" id="node-input-OnTimeoutVal" placeholder="Time-Out Value" style="text-align:end; width:245px !important">
</div>
<div class="form-row">
<label style="width: 180px !important" for="node-input-passthrough"><i class="fa fa-sign-out"></i> Pass-through messages</label>
<input type="checkbox" id="node-input-passthrough" style="margin-left: 0px; vertical-align: middle; width: auto !important;">
<input type="checkbox" id="node-input-passthrough" style="margin-left: 0px; vertical-align: middle; width: auto !important;">
</div>
<div class="form-row">
<label style="width: 180px !important" for="node-input-sendTimeoutValue"><i class="fa fa-refresh"></i> Send Time-Out Value</label>
<input type="text" id="node-input-sendTimeoutValue" style="text-align:end; width:245px !important">
</div>
<div class="form-row" id="node-input-interval-row" style="display: none;">
<label style="width: 180px !important" for="node-input-interval"><i class="fa fa-clock-o"></i> Interval</label>
<input type="text" id="node-input-interval" placeholder="Interval" style="text-align:end; width:100px !important">
<input type="text" id="node-input-IntervalUnits" style="width:140px !important">
</div>
<div class="form-row">
<label style="width: 180px !important" for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name" style="width:245px !important"></input>
<input type="text" id="node-input-name" placeholder="Name" style="width:245px !important"></input>
</div>
</script>

<script type="text/x-red" data-help-name="timeouttrigger">
<p>Pipes <i>any</i> <code>msg</code> while resetting a time-out. Upon time-out a defined value is send.
Turning off <code>Pass-through messages</code> will stop piping the incoming messages, so only the time-out values is send.</p>
Turning off <code>Pass-through messages</code> will stop piping the incoming messages, so only the time-out value is send.
Setting <code>Send Time-Out Value</code> to <code>Continuously</code> will repeat the time-out value at the given interval.</p>
</script>
115 changes: 65 additions & 50 deletions timeouttrigger.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,72 +16,87 @@

module.exports = function(RED) {
'use strict';
function TimeOutTrigger(n) {
RED.nodes.createNode(this, n);

this.units = n.units || 's';
this.duration = n.duration || 5;
this.ontimeoutval = n.ontimeoutval || '0';
this.ontimeouttype = n.ontimeouttype || 'str';
this.passthrough = n.passthrough ?? true; // default to true if value is nullish

if (this.duration <= 0) {
this.duration = 0;
} else {
if (this.units === 's') {
this.duration *= 1000;
}
if (this.units === 'min') {
this.duration = this.duration * 1000 * 60;
}
if (this.units === 'hr') {
this.duration = this.duration * 1000 * 60 * 60;
}
}

class TimeOutTrigger {
constructor(config) {
RED.nodes.createNode(this, config);

this.units = config.units || 's';
this.duration = Number(config.duration) || 5;
this.OnTimeoutVal = config.OnTimeoutVal || '0';
this.OnTimeoutType = config.OnTimeoutType || 'str';
this.passthrough = config.passthrough ?? true;
this.sendTimeoutValue = config.sendTimeoutValue ?? 'once';
this.interval = Number(config.interval) || 5;
this.intervalUnits = config.IntervalUnits || 's';

this.duration *= this.getMultiplier(this.units);
this.interval *= this.getMultiplier(this.intervalUnits);

if ((this.ontimeouttype === 'num') && (!isNaN(this.ontimeoutval))) {
this.ontimeoutval = Number(this.ontimeoutval);
} else if (this.ontimeoutval === 'true' || this.ontimeoutval === 'false') {
(this.ontimeoutval === 'true' ? this.ontimeoutval = true : this.ontimeoutval = false);
} else if (this.ontimeoutval === 'null') {
this.ontimeouttype = 'null';
this.ontimeoutval = null;
} else {
this.ontimeoutval = String(this.ontimeoutval);
if (this.OnTimeoutType === 'num' && !isNaN(this.OnTimeoutVal)) {
this.OnTimeoutVal = Number(this.OnTimeoutVal);
} else if (this.OnTimeoutVal === 'true' || this.OnTimeoutVal === 'false') {
this.OnTimeoutVal = (this.OnTimeoutVal === 'true');
} else if (this.OnTimeoutVal === 'null') {
this.OnTimeoutType = 'null';
this.OnTimeoutVal = null;
}

this.on('input', this.handleInput);
this.on('close', this.handleClose);
}

var node = this;
var tout = null;
getMultiplier(unit) {
switch (unit) {
case 's': return 1000;
case 'min': return 1000 * 60;
case 'hr': return 1000 * 60 * 60;
default: return 1;
}
}

this.on('input', function(msg, send, done) {
handleInput(msg, send, done) {
send = send || function() {
node.send.apply(node, arguments);
this.send.apply(this, arguments);
};

if (this.passthrough) {
send(msg);
}
clearTimeout(tout);
node.status({fill:'green', shape:'dot'});
tout = setTimeout(function() {
var msg2 = RED.util.cloneMessage(msg);
msg2.payload = node.ontimeoutval;

clearTimeout(this.tout);
clearInterval(this.reTrigger);

this.status({ fill: 'green', shape: 'dot' });

const sendMessage = () => {
const msg2 = RED.util.cloneMessage(msg);
msg2.payload = this.OnTimeoutVal;
send(msg2);
tout = null;
node.status({fill:'red', shape:'ring', text:'timed out'});
}, node.duration);
};

this.tout = setTimeout(() => {
this.status({ fill: 'red', shape: 'ring', text: 'timed out' });

if (this.sendTimeoutValue === 'once') {
sendMessage();
} else if (this.sendTimeoutValue === 'continuously') {
sendMessage();
this.reTrigger = setInterval(sendMessage, this.interval);
}
}, this.duration);

if (done) {
done();
}
});
}

this.on('close', function() {
if (tout) {
clearTimeout(tout);
}
node.status({});
});
handleClose() {
clearTimeout(this.tout);
clearInterval(this.reTrigger);
this.status({});
}
}

RED.nodes.registerType('timeouttrigger', TimeOutTrigger);
};

0 comments on commit 1abb2d1

Please sign in to comment.